// how pieces are situated in map of pieces
var ACD_PIECESMAP_COLUMNS = 4;
var ACD_PIECESMAP_ROWS = 7;

// spacing between mouse pointer and hover diagram
var ACD_DIAGRAM_LEFT = 30;
var ACD_DIAGRAM_TOP = 30;

// ShadowC's mod Here is an object that presents a list of objects with a fen string asociated with them
var acd_fen_registers = Array();

// ShadowC function acd_register_fen() will add an object to the acd_fen_registers
function acd_register(idStr, fenStr) {
    acd_fen_registers[acd_fen_registers.length] = {id: idStr, fen: fenStr};
}

//-------------------------- PUBLIC EVENT HANDLERS -----------------------------

// event handler of ONMOUSEOVER event to be used while embedding to links
/*
    ev - current event
    acd - object representing component
    link - object representing current link
*/
function acd_hover(ev, acd, link) {
  // hover diagram to be shown only if required images are already loaded
  if (!acd.empty_diagram.complete) return;
  if (!acd.pieces_map.complete) return;

  if (acd.rollover_class && link.className != acd.rollover_class) return;

  link.onmouseout = _acd_hide_board;
  link.diagram = acd;

  var url = new String(link.href);
  url = url.replace(/\%20/gi, " ");

  ev = ev || window.event;
  x = ev.clientX;
  y = ev.clientY;
  if (!ev.pageX) {
      l = document.documentElement.clientLeft;
  }
  else {
      l = x + document.documentElement.scrollLeft - ev.pageX;
  }
  if (!ev.pageY) {
      t = document.documentElement.clientTop;
  }
  else {
      t = y + document.documentElement.scrollTop - ev.pageY;
  }

  /* Code added by ShadowC to force white on top or black on top upon a given id in the link object */
  var forceSide = "default";

  if (link.id.substring(0,5) == "white")
    forceSide = "white";
  if (link.id.substring(0,5) == "black")
    forceSide = "black";
  /* End of ShadowC's mod */

  
    /* Modded by ShadowC to take a fen string from a javascript array
          var fen = acd_get_parameter(url, "fen");
          if (fen == null) return; */

  var fen;
  var linkWords = link.id.split('_');

  for (var n=0; n < acd_fen_registers.length; n++) {
      if (acd_fen_registers[n].id == linkWords[linkWords.length-1]) {
        fen = acd_fen_registers[n].fen;
        break;
      }
  }

  if (fen) {
      acd.popup(x, y, l, t); 
      acd.position(fen, "", forceSide);
      acd.diagram.style.visibility = "visible";
  }
  /* End of ShadowC's code */
  else {  
    if (!acd.rollover_server) {
        acd.popup(x, y, l, t); 
        acd.position("", "No diagram");
        acd.diagram.style.visibility = "visible";
    } else {
      
      var id = acd_get_parameter(url, "id");
      if (id == null) return;
  
      if (acd.request) {
          acd.request.onreadystatechange = function() {};
          acd.request.abort();
      }
  
      request = null;
      if (window.XMLHttpRequest)
          request = new XMLHttpRequest();
      else if (window.ActiveXObject)
          request = new ActiveXObject("Microsoft.XMLHTTP");
  
      if (request == null) return;
      acd.request = request;
  
      acd.popup(x, y, l, t);
      acd.position("", "Loading ...");
      acd.diagram.style.visibility = "visible";
  
      request.onreadystatechange = function() {
          switch (request.readyState) {
          case 4:
              switch (request.status) {
              case 200:
                  var fen = acd_get_parameter(request.responseText, "fen");
                  if (fen == null) fen = request.responseText;
                  if (fen == "") {
                      acd.position("", "No diagram");
                      return;
                  }
  
      // modded by ShadowC added the forceSide parameter
                  
                  acd.position(fen, "", forceSide);
                  break;
              default:
                  acd.diagram.style.visibility = "hidden";
                  break;
              }
              break;
          }
      };
      var d = new Date();
      var u = acd.rollover_server + "?id=" + id + "&timestamp=" + d.valueOf();
      request.open("GET", u, true);
      request.send(null);
    }
  }
}

//---------------------------- CONSTRUCTOR -------------------------------------

/*
    id - ID of diagram DIV
        null - look for DIV of class "acd" for diagram (to be child of body)
        not null - use this DIV for diagram
    upsidedown - if board oriented upsidedown
        false - white are on bottom
        true - white are on top
    pieces_count - maximum number of pieces assumed to be on the board
        0 - default number (32) of cells and pieces to be created automatically
        >0 - requested number of cells and pieces to be created automatically
*/
function active_chess_diagram(id, cell_width, cell_height, upsidedown, pieces_count) {
    // public methods
    this.rollover = _acd_rollover;
    this.position = _acd_position;

    // private functions
    this.prepare = _acd_prepare;
    this.popup = _acd_popup;

    this.id = id;
    this.cell_width = cell_width;
    this.cell_height = cell_height;
    this.pieces_count = pieces_count;
    this.upsidedown = upsidedown;

    var i;
    if (!this.id) {
        var nodes = document.documentElement.childNodes;
        for (i = 0; i < nodes.length; i++) {
            if (nodes[i].className == "acd") {
                this.diagram = nodes[i];
                break;
            }
        }
    } else this.diagram = document.getElementById(this.id);

    var groups = this.diagram.childNodes;
    for (i = 0; i < groups.length; i++) {
        if      (groups[i].className == "empty_diagram")    this.empty_diagram  = groups[i];
        else if (groups[i].className == "pieces_map")       this.pieces_map     = groups[i];
        else if (groups[i].className == "board")            this.board          = groups[i];
        else if (groups[i].className == "caption")          this.caption        = groups[i];
    }

    if (!this.cell_width) this.cell_width = 20;
    if (!this.cell_height) this.cell_height = 20;
    if (!this.pieces_count) this.pieces_count = 32;

    this.request                = null;     // track XMLHttpRequest to have just one per object
    this.board.style.overflow   = "hidden"; // mandatory

    this.prepare();
}

//---------------------------- PUBLIC METHODS ----------------------------------

// bind diagram to links for rollower effect
/*
    rollover_handler - if to use handler embedded into links or bind to links automatically
        false - bind to links automatically
        true - use ONMOUSEOVER event handlers embedded into links
    rollover class - links of what class to bind to
        null - bind to all the links
        not null - bind just to links of this class
    rollover_server - URL of server to get FENs from
        null - use fen="..." of link's HREF
        not null - get FENs from the server
*/
function _acd_rollover(rollover_handler, rollover_class, rollover_server) {
    this.diagram.style.position     = "absolute";   // mandatory
    this.diagram.style.visibility   = "hidden";     // mandatory

    this.rollover_handler           = rollover_handler;
    this.rollover_class             = rollover_class;
    this.rollover_server            = rollover_server;

    if (!this.rollover_handler) {
        var len = document.links.length;
        var links = document.links;
        var link;

        if (!this.rollover_class) {
            var i;
            for (i = 0; i < len; i++) {
                link = links[i];

                link.onmouseover = _acd_show_board;
                link.diagram = this;
            }
        } else {
            var i;
            for (i = 0; i < len; i++) {
                link = links[i];
                if (link.className != this.rollover_class) continue;

                link.onmouseover = _acd_show_board;
                link.diagram = this;
            }
        }
    }

    var self = this;
    this.diagram.onmouseover = function (ev) {
        self.diagram.style.visibility = "hidden";
    }
    this.diagram.onmouseout = function (ev) {
        self.diagram.style.visibility = "hidden";
    }
}

// set position on diagram
/*
    FEN - string with position's notation
    caption - position's caption

    Mod by ShadowC: forceSide parameter to force a color on the bottom of the board
*/
function _acd_position(fen, caption, forceSide) {
    var FEN = new String(fen);
    FEN = FEN.replace(/\%20/gi, " ");
    var len = FEN.length;

    var x = 0;
    var y = 0;
    var count = 0;
    var dopiece = false;
    var i;

    /* ShadowC mod to determine if black moves so we automatically show the board upside-down */
  var fenSwap = false;
  if (FEN) {
      var fenFields = FEN.split(" ");
      if (fenFields.length > 1) {
          if (fenFields[1].toLowerCase() == "b")
            fenSwap = true;
      }
        }

  if (forceSide) {
      switch (forceSide) {
    case ('white'):
        this.upsidedown = false;
        break;
    case ('black'):
        this.upsidedown = true;
        break;
    case ('default'):
        if (fenSwap)
      this.upsidedown = true;
        else
      this.upsidedown = false;
        break;
      }
  }
  else {
      if (fenSwap)
    this.upsidedown = true;
      else
    this.upsidedown = false;
  }

    /* End of ShadowC's Code */

    for (i = 0; i < len && y < 8 && count < this.pieces_count; i++) {
        if (x >= 8) x = 0;
        switch (FEN.charAt(i)) {
            default: break;

            case '/':
                y++;
                x = 0;
                break;
            case ' ':
                y = 8;
                break;

            case '1': x += 1; break;
            case '2': x += 2; break;
            case '3': x += 3; break;
            case '4': x += 4; break;
            case '5': x += 5; break;
            case '6': x += 6; break;
            case '7': x += 7; break;
            case '8': x += 8; break;

            case 'k':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -1;
                break;
            case 'q':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -2;
                break;
            case 'r':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -3;
                break;
            case 'b':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -4;
                break;
            case 'n':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -5;
                break;
            case 'p':
                dopiece = true;
                this.pieces_left[count] = -1;
                this.pieces_top[count] = -6;
                break;

            case 'K':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -1;
                break;
            case 'Q':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -2;
                break;
            case 'R':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -3;
                break;
            case 'B':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -4;
                break;
            case 'N':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -5;
                break;
            case 'P':
                dopiece = true;
                this.pieces_left[count] = 0;
                this.pieces_top[count] = -6;
                break;
        }

        if (dopiece) {
            this.cells_left[count] = !this.upsidedown ?
    x * this.cell_width  : (7 - x) * this.cell_width;

            this.cells_top[count] = !this.upsidedown ?
                y * this.cell_height : (7 - y) * this.cell_height;

           /* this.pieces_left[count] += !this.upsidedown ?
                this.diagonal[(x + y + 1) % 2] : this.diagonal[(x + 8 - y) % 2];*/

      this.pieces_left[count] +=  this.diagonal[(x + y + 1) % 2];

            this.pieces_left[count] *= this.cell_width;
            this.pieces_top[count] *= this.cell_height;

            count++; x++;
            dopiece = false;
        }
    }

    for (i = 0; i < count; i++) {
        this.pieces[i].style.left = this.pieces_left[i] + "px";
        this.pieces[i].style.top = this.pieces_top[i] + "px";
        this.cells[i].style.left = this.cells_left[i] + "px";
        this.cells[i].style.top = this.cells_top[i] + "px";
    }

    for (i = count; i < this.pieces_count; i++) {
        this.cells[i].style.left = -1 * this.cell_width + "px";
        this.cells[i].style.top = -1 * this.cell_height + "px";
    }

    if (caption != null) this.caption.innerHTML = caption;
}

//----------------------------- PRIVATE FUNCTIONS ------------------------------

// prepare diagram DIV
function _acd_prepare() {
    this.cells          = new Array(this.pieces_count);
    this.pieces         = new Array(this.pieces_count);

    this.diagonal       = new Array(0, -2);
    this.cells_left     = new Array(this.pieces_count);
    this.cells_top      = new Array(this.pieces_count);
    this.pieces_left    = new Array(this.pieces_count);
    this.pieces_top     = new Array(this.pieces_count);

    var pm_width    = this.cell_width * ACD_PIECESMAP_COLUMNS;
    var pm_height   = this.cell_height * ACD_PIECESMAP_ROWS;

    var i;
    for (i = 0; i < this.pieces_count; i++) {
        this.cells[i] = document.createElement('div');
        this.cells[i].style.position    = "absolute";
        this.cells[i].style.overflow    = "hidden";
        this.cells[i].style.width       = this.cell_width + "px";
        this.cells[i].style.height      = this.cell_height + "px";

        this.pieces[i] = document.createElement('img');
        this.pieces[i].style.position   = "absolute";
        this.pieces[i].style.width      = pm_width + "px";
        this.pieces[i].style.height     = pm_height + "px";
        this.pieces[i].style.borderWidth= "0";       // Firefox needs this
        this.pieces[i].src              = this.pieces_map.src;

        this.cells[i].appendChild(this.pieces[i]);
        this.board.appendChild(this.cells[i]);
    }
}

// place diagram at best position near the mouse pointer
/*
    xo - client X of mouse
    yo - client T of mouse
    l - client left of body
    t - client top of body
*/
function _acd_popup(xo, yo, l, t) {
    if (xo + this.diagram.clientWidth + ACD_DIAGRAM_LEFT < document.documentElement.clientWidth)
        xo +=  ACD_DIAGRAM_LEFT;
    else
        xo -= this.diagram.clientWidth + ACD_DIAGRAM_LEFT;

    if (yo - this.diagram.clientHeight / 2 - ACD_DIAGRAM_TOP > 0 && yo + this.diagram.clientHeight / 2  + ACD_DIAGRAM_TOP < document.documentElement.clientHeight)
        yo -=  this.diagram.clientHeight / 2;
    else {
        if (yo - this.diagram.clientHeight / 2 - ACD_DIAGRAM_TOP <= 0)
            yo = ACD_DIAGRAM_TOP;
        else
            yo = document.documentElement.clientHeight - this.diagram.clientHeight - ACD_DIAGRAM_TOP;
    }

    x = xo + document.documentElement.scrollLeft - l;
    y = yo + document.documentElement.scrollTop - t;

    this.diagram.style.left = x + "px";
    this.diagram.style.top = y + "px";
}

//-------------------------- PRIVATE EVENT HANDLERS ----------------------------

// event handler of ONMOUSEOVER event binded to link at automatic binding
/*
    ev - current event
*/
function _acd_show_board(ev) {
    acd_hover(ev, this.diagram, this);
}

// event handler of ONMOUSEOUT event binded to link in ONMOUSEOVER handler
/*
    ev - current event
*/
function _acd_hide_board(ev) {
    this.diagram.diagram.style.visibility = "hidden";
}

//-------------------------- HELPER FUNCTIONS ----------------------------------

// get parameter from URL, assuming to be key=value pair
/*
    url - url to extract parameter from
    key - key to look for

    return - parameter value
*/
function acd_get_parameter(url, key) {
    var s = new String(url);
    var begin;
    var end;
    var id_parsed;

    begin = s.indexOf("?" + key + "=", 0);
    if (begin < 0) begin = s.indexOf("&" + key + "=");

    if (begin < 0) {
      begin = s.indexOf("(", 0);
      if(begin < 0) {
        return null;
      }
      end = s.indexOf(")");
      begin = begin + 1;
      id_parsed = s.substr(begin, end - begin);
      return id_parsed;
    }

    begin += key.length + 2;
    end = s.indexOf("&", begin + key.length + 2);
    if (end < 0) end = s.length;

    id_parsed = s.substr(begin, end - begin);
    return id_parsed;
}

