// Author: Juan Carlos De Abreu (jcabreur@gmail.com)

#include "window_management.h"
#include "../Includes/OpenGL.h"
#include "../Util/TextureManagement.h"

#include "text.h"
#include "panel.h"
#include "check_box.h"
#include "radio_button.h"

BEGIN_TEG_NAMESPACE();
BEGIN_NAMESPACE(gui);

void WindowManagement::glWrite(int x, int y, const char *str) {
  glColor3f(0,0,0);

  // Select the texture to be use
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, fontTexture);

  glPushMatrix();
    glTranslated(x, - y, 0.2);

    glListBase(fontList);
    glCallLists(strlen(str), GL_BYTE, str);
  glPopMatrix();

  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);
}

bool WindowManagement::buildFont(const char *pathFontWidth) {
  // Load the width of the each character ..
  BYTE fontWidth[256];

  FILE *f = fopen(pathFontWidth,"r");	
  if (f == NULL) { 
    // Report a warning in the log
    return false;
  }
  fread(fontWidth, sizeof(fontWidth), 1, f);
  fclose(f);

  // Creating 256 display list
  fontList = glGenLists(256);
	
  // Select the font texture
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, fontTexture);
	
  // Loop to store each character in a display list
  for (int i = 0; i < 256; ++i) {
    // Calculate the current position of the character
    float cx = static_cast<float>(i % 16) / 16;
    float cy = static_cast<float>(i / 16) / 16;

    fontWidth[i] = 6;

    GLfloat xs = (float)(16 - fontWidth[i]) / 2.0f / 256.0f;

    // Create the i character
    glNewList(fontList + i, GL_COMPILE);
      glBegin(GL_QUADS);
        glTexCoord2f(cx + xs, 1 - cy - 0.0625f);
        glVertex2i(0, 0);

        glTexCoord2f(cx + xs + (float)fontWidth[i]/256.f + 1.f/512.f, 1 - cy - 0.0625f);
        glVertex2i(fontWidth[i], 0);

        glTexCoord2f(cx + xs + (float)fontWidth[i]/256.f + 1.f/512.f, 1 - cy);
        glVertex2i(fontWidth[i], 16);

        glTexCoord2f(cx + xs, 1 - cy);
        glVertex2i(0, 16);
      glEnd();
      glTranslated(fontWidth[i] + 1,0,0);
    glEndList();
  }

  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);

  return true;
}

void drawMouse(const WindowManagement* pWM) { 	
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, GUI_TEXTURE);

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
  glEnable(GL_BLEND);
  glEnable(GL_DEPTH_TEST);
  glColor4f(1,1,1,0.9f);
	
  glBegin(GL_QUADS);
    glTexCoord2f(0.3125f, 0.5f);	glVertex3f(MOUSE_X + 0.0f, HEIGHT_SCREEN - MOUSE_Y  + 0.0f, 1.0f);
    glTexCoord2f(0.5625f, 0.5f);	glVertex3f(MOUSE_X + 32.0f, HEIGHT_SCREEN - MOUSE_Y + 0.0f, 1.0f);
    glTexCoord2f(0.5625f, 0.25f);	glVertex3f(MOUSE_X + 32.0f, HEIGHT_SCREEN - MOUSE_Y - 32.0f, 1.0f);
    glTexCoord2f(0.3125f, 0.25f);	glVertex3f(MOUSE_X + 0.0f, HEIGHT_SCREEN - MOUSE_Y - 32.0f, 1.0f);
  glEnd();

  glBlendFunc(GL_ONE, GL_ONE);

  glDisable(GL_BLEND);

  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);
}

class LessOperatorWindow {
 public:
  bool operator () (const Window& w1, const Window& w2) {
    return w1.z_order() < w2.z_order();
  }
};

bool WindowManagement::onMouseDown(Window *window) {
  IS_MOUSE_DRAG = false;

  bool wndClick = false;
  int mouseX, mouseY;

  if (!window->is_visible())
    return false;

  mouseX = MOUSE_X;
  mouseY = HEIGHT_SCREEN - MOUSE_Y;
	
  // Check if the user did click inside the window
  if (mouseX > window->x() && mouseX < window->x() + window->width()) {	
    if (mouseY < window->y() && mouseY > window->y() - window->height()) {
      wndClick = true;
    }
  }

  if (!wndClick) return false;

  // Change the position of the window to draw over all other window
  for (int i = 0; i < (int)windows_.size(); ++i) {
    if (windows_[i].z_order() > window->z_order())
      windows_[i].set_z_order(windows_[i].z_order() - 1);
  }
  window->set_z_order((float)windows_.size() - 1);

  std::sort(windows_.begin(), windows_.end(), LessOperatorWindow());

  window = &windows_[windows_.size() - 1];
	
  // Calculate the coordinates of mouse relatives to the window
  mouseX = mouseX - window->x();
  mouseY = window->y() - mouseY;

  // Check if the user did click over tittle bar
  if (mouseY <  26) {
    wndClick = true;

    // Check if the user did click in close icon
    if (mouseX > window->width() - 22 && mouseX < window->width() - 6){
      window->set_is_visible(false);
    } else {
      // The user did click in the bar tittle
      IS_MOUSE_DRAG = true;
      MOUSE_DRAG_X = mouseX; //mouse->x - window.x;
      MOUSE_DRAG_Y = HEIGHT_SCREEN - MOUSE_Y - window->y();
      MOUSE_DRAG_WINDOW = window;
    }
    return true;
  } else {
    // Obtain the number of buttons in the window
    const int nButtons = static_cast<int> (window->button().size());
    const int nCkeckBoxs = static_cast<int> (window->check_box().size());
    const int nRadioButtons = static_cast<int> (window->radio_button().size());
    const int nPanels = static_cast<int> (window->panel().size());

    // Check if the user did click over a button
    for (int i = 0; i < nButtons; ++i) {
      if (mouseX > window->button()[i].x() && mouseX < window->button()[i].x() + window->button()[i].width()) {
        if (mouseY > window->button()[i].y() && mouseY < window->button()[i].y() + window->button()[i].height()) {
          window->button()[i].set_is_pressed(true);
          buttonPressed = &window->button()[i];
          window->button()[i].onClick();
          return true;
        }
      }
    }
		
    // Check if the user did click over a checkBox
    for (int i = 0; i < nCkeckBoxs; ++i) {
      if (mouseX > window->check_box()[i].x() && mouseX < window->check_box()[i].x() + 16) {
        if (mouseY > window->check_box()[i].y() && mouseY < window->check_box()[i].y() + 16) {			
          window->check_box()[i].set_is_checked(!window->check_box()[i].is_checked());
          return true;
        }
      }
    }

    // Check if the user did click over a radioButton
    for (int i = 0; i < nRadioButtons; ++i) {
      if (mouseX > window->radio_button()[i].x() && mouseX < window->radio_button()[i].x() + 16) {
        if (mouseY > window->radio_button()[i].y() && mouseY < window->radio_button()[i].y() +16) {
          // UnCheck the others radioButtons of the same group
          for (int j = 0; j < nRadioButtons; ++j) {
            if (window->radio_button()[j].group() == window->radio_button()[i].group())
              window->radio_button()[j].set_is_checked(false);
          }
          // Check this radioButton
          window->radio_button()[i].set_is_checked(true);
          return true;
        }
      }
    }
  }

  // If is here is because the use click over the window.
  return true;
}

void WindowManagement::onMouseUp() {
  IS_MOUSE_DRAG = false;
  MOUSE_BUTTON = 0;

  if (buttonPressed != NULL) {
    buttonPressed->set_is_pressed(false);
    buttonPressed->onClick();
  }
}

WindowManagement::WindowManagement() {
  GUITexture = 0;
  fontTexture = 0;
  buttonPressed = NULL;
//  is_console_enable_ = true;
}

WindowManagement::~WindowManagement() {
  windows_.clear();
  glDeleteLists(fontList, 256);
}

void WindowManagement::render() {
  glPushMatrix();

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, WIDTH_SCREEN, 0, HEIGHT_SCREEN, 0, 100);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glTranslatef(0, 0, -10);

  for (int i=0;i<(int)windows_.size();i++) {
    glTranslatef(0,0,1);
    windows_[i].Render();
  }

  if (is_console_enable_) {
    console_.set_cont((console_.cont() + 1) % 60);
    console_.Render();
  }

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0, WIDTH_SCREEN / HEIGHT_SCREEN, 1.0, 100.0);
  glMatrixMode(GL_MODELVIEW);

  glPopMatrix();
}

bool WindowManagement::loadConfig() {
  FILE* f = fopen("GUIConfig.txt","r");
  if (f == NULL) return false;

  char pathGUITexture[256];
  char pathFontTexture[256];
  char pathFontWidth[256];

  fgets(pathGUITexture, 256, f);
  sscanf(pathGUITexture, "%s", pathGUITexture);

  fgets(pathFontTexture, 256, f);
  sscanf(pathFontTexture, "%s", pathFontTexture);

  fgets(pathFontWidth, 256, f);
  sscanf(pathFontWidth, "%s", pathFontWidth);

  GUITexture = LOAD_TEXTURE(pathGUITexture);
  fontTexture = LOAD_TEXTURE(pathFontTexture);

  buildFont(pathFontWidth);

  fclose(f);
  return true;
}

void WindowManagement::addWindow(Window window) {
  window.set_z_order((float)windows_.size());
  windows_.push_back(window);	
}

END_NAMESPACE(gui);
END_TEG_NAMESPACE();
