var ly_cont;
var DIR = { ACROSS: 0, LONG: 1, FORWARD: 2, BACKWARD: 3, UNDEFINED: 4 };
var DIR_DISPLAY = { 0:"ACROSS", 1:"LONG", 4:"UNDEFINED" };
var goingDir = DIR.ACROSS;
var SQ = null; // the input of the square focus is on
var wasSQ = null;  // the input of the square focus was before current

// since focus() fires before click(), need another var to track which sq was
// previously clicked
var wasClickSQ = null;

var QU_ID = null; // current question id
var wasQU_ID = null; // id of the last question

var KEEP_HIGHLIT_ELEMENT = false;

document.onkeydown = function(e) {

  // regardless of whether any square is selected

  //alert(e);

  var code = -1;

  try {
    code = e.which;
  } catch (e) {
    if (e.keyCode) {
      code = e.keyCode;
    } else if (window.event.keyCode) {
      code = window.event.keyCode;
    }
  }

  if (code == KBD.ESC) {
    escapePressed();
  }

  // only when a square is selected

  if (SQ == null) {
    return;
  }

  if (code >= KBD.LEFT && code <= KBD.DOWN) {
    arrowPressed(code);
    return false;
  //} else if (code == KBD.ESC) {
  //  SQ.blur();
  } else if (code == KBD.BACKSPACE) {
    backspacePressed(SQ);
  } else if (code == KBD.DELETE) {
    deletePressed(SQ);
  } else if (code == KBD.SPACE) {
    spacePressed();
  }
}

$(document).ready(function() {
  CONTEXT_PATH = $("#context_path_value").val();

  ly_cont = $("#ly_wrap");
  initStarts();
  initSquares();
  initQuestions();

  putGlyphs();

  darkenGlueBtn();
  darkenSawBtn();


  goingDir = DIR.ACROSS;
  displayDirection(goingDir);

  logg("CONTEXT_PATH=" + CONTEXT_PATH);

  preloadGlyphs();
});

var initStarts = function() {
  ly_cont.find(".el_st").click(function() {
    var dis = $(this);
  });
}

var initSquares = function() {
  logg("initSquares");
  ly_cont.find(".el")
      .unbind("keypress")
      .unbind("focus")
      .unbind("blur")
  .keypress(function(e) {
    var sq = $(this);
    var character = processSquare(sq, e);
    sq.val(character);
    return false;
  }).focus(function() { squareFocus($(this));
  }).blur(function() {  squareBlur();
  }).click(function() {
    //goingDir = DIR.LONG; // this will be inverted to ACROSS if possible
//    if (SQ != null && SQ.is("input") && SQ.parent().hasClass("hl")) {
      // now check whether this is the first click
//      if (wasClickSQ != null && wasClickSQ.attr("id") == SQ.attr("id")) {
//        spacePressed();
//      }
//      wasClickSQ = SQ;
//    }
  });
}

/**
 *
 */
var squareFocus = function(el) {
  /* SQUARE FOCUS */
  logg("---SQUARE FOCUS---");
  var setGoingDir = goingDir;
  logg("goingDir: " + goingDir);

  // setting going direction:
  // if square has two directions (a crossing), do nothing
  // otherwise, force the single one

  if (el.hasClass("d_acro") && el.hasClass("d_long") == false) {
    setGoingDir = DIR.ACROSS;
  } else if (el.hasClass("d_long") && el.hasClass("d_acro") == false) {
    setGoingDir = DIR.LONG;
  }

  highlightSquare(el);
  wasSQ = SQ == null ? el : SQ; // whoops!
  SQ = el;

  $("#glue_dir_selector").remove();

  if (allowGluing(el, DIR.ACROSS) || allowGluing(el, DIR.LONG)) {
    highlightGlueBtn();
  } else {
    darkenGlueBtn();
  }

  if (allowSawing(el, DIR.ACROSS)) {
    highlightSawBtn();
  } else {
    darkenSawBtn();
  }

  // restoring going direction

  goingDir = setGoingDir; // default

  if (el.hasClass("d_acro") == false && el.hasClass("d_long")) {
    goingDir = DIR.LONG;
  } else if (el.hasClass("d_acro") && el.hasClass("d_long")) {
    // crossing: checking which was previous
    if (wasSQ != null && wasSQ.is("input") && wasSQ.attr("id") != SQ.attr("id")
        && wasSQ.hasClass("d_long") && wasSQ.hasClass("d_acro") == false) {
      logg("was sq (" + wasSQ.attr("id") + ") has class long, sq: " + SQ.attr("id") + " ");
      goingDir = DIR.LONG;
    }

  }

  logg("gd now: " + goingDir);

  displayDirection(goingDir);
}

/**
 * Verifies whether it's allowed to glue an element in give direction. There are
 * multiple factors to check:
 * 1. Whether there any glue-s available;
 * 2. Whether next square isn't an enabled square (ensq);
 * 3. Whether the square next to the next isn't an ensq (otherwise two words
 *    would merge);
 * -- yet unimplemented--
 * 4. Whether the to-be-glued square's position will make it uncertain what
 *    element does it belong to.
 *
 * Parameters:
 *
 * el: the square to glue to
 *
 * dir: direction to glue in
 */
var allowGluing = function(el, dir) {

  logg("allowGluing(" + el.attr("id") + ", " + DIR_DISPLAY[dir] + ")");
  var allow = false;

  if (GS_COUNT < 1) {
    logg("FALSE");
    return false;
  }

  var setGoingDir = goingDir;

  var assertOnly = true;
  var next; // next element in selected direction
  var sqi = parseInt(el.attr("id").split("_")[1]); // square id
  var xLen = parseInt($("#ly_x_len").val())

  if (dir == DIR.ACROSS) {
    logg("verifying across");
    goingDir = DIR.ACROSS;
    next = nextSquare(el.attr("class"), DIR.FORWARD, assertOnly);
    // first: whether next square isn't an input
    if (el.hasClass("d_acro") &&
        (next == null || next.is("input") == false)) {
      var wrap = el.parent(); // wrapping span
      // second: whether a next-next element isn't a square
      if (wrap.next().next().find("input").eq(0).hasClass("el") == false) {
        // third: no ensq-s to the top and bottom of the to-be-glued square
        var top = $("#sqi_" + ((sqi-xLen)+1));
        var bottom = $("#sqi_" + ((sqi+xLen)+1));
        //logg("top: " + "#sqi_" + ((sqi-xLen)+1) + ", bottom: " + ((sqi+xLen)+1))
        if (
            (top.is("input") == false || top.hasClass("el") == false)
            && (bottom.is("input") == false || bottom.hasClass("el") == false)) {
          allow = true;
        }
      }
    }
  }

  if (dir == DIR.LONG) {
    logg("verifying long");
    goingDir = DIR.LONG;
    next = nextSquare(el.attr("class"), DIR.FORWARD, assertOnly);
    // first: whether next square isn't an input
    if (el.hasClass("d_long") &&
        (next == null || next.is("input") == false)) {
      // second: whether a next-next square isn't an input
      // going vertically, next-next square is current square id
      // plus x-length * 2
      var nextNext = $("#sqi_" + (sqi+xLen*2));
      if (nextNext.is("input") == false || nextNext.hasClass("el") == false) {
        // third: no ensq-s to the left and right of the to-be-glued square
        var left = $("#sqi_" + ((sqi+xLen)-1));
        var right = $("#sqi_" + ((sqi+xLen)+1));
        if (
            (left.is("input") == false || left.hasClass("el") == false)
            && (right.is("input") == false || right.hasClass("el") == false)) {
          allow = true;
        }
      }
    }
  }

  goingDir = setGoingDir;

  logg(allow)
  return allow;
}

var allowSawing = function(el, dir) {
  logg("allowSawing(" + el.attr("id") + ", " + DIR_DISPLAY[dir] + ")");
  if (GS_COUNT < GS_MAX && el.hasClass("el_st") == false) {
    logg("TRUE");
    return true;
  }
  logg("FALSE");
  return false;
}

var squareBlur = function() {
  logg("---SQUARE BLUR---");
  if (SQ != null && SQ.is("input")) {
    SQ.parent().removeClass("hl");
  }
  wasSQ = SQ;
  SQ = null;

  wasQU_ID = QU_ID;
  QU_ID = 0;

  darkenGlueBtn();
  darkenSawBtn();
  darkenElements();
}

var initQuestions = function() {
  $("#qu").find("li>span").click(function() {
    var qu = $(this);
    var el_id = qu.parent().attr("id").split("_")[1];

    var qListId = qu.parent().parent().attr("id");
    if (qListId == "q_list_across") {
      goingDir = DIR.ACROSS;
    } else if (qListId == "q_list_down") {
      goingDir = DIR.LONG;
    }

    ly_cont.find(".el_st_" + el_id).eq(0).focus();
  });
}

var escapePressed = function() {
  logg("escapePressed");
  KEEP_HIGHLIT_ELEMENT = false;
  darkenElements();
  $(".enter_wrap").hide().find(".enter_text").val("");

  $("#glue_dir_selector").remove();

  $("#add_question_wrap").hide();
  hideLoginForm();
  hideForgotForm();
  hideRegisterForm();

  if (SQ != null) {
    SQ.blur();
  }

  hideSavedCont();

}

var backspacePressed = function(el) {
  var prev = nextSquare(el.attr("class"), DIR.BACKWARD, false);
  var next = nextSquare(el.attr("class"), DIR.FORWARD, false);
  if (el.attr("value").trim().length < 1) {
    prev.attr("value", "");
    scrapeGlyph(prev);
    prev.focus();
    //SQ = prev;  // just in case?
  } else {
    el.attr("value", "");
    scrapeGlyph(el);
    if (next.is("input")) {
      prev.focus();
      //SQ = prev;
    }
  }

  return false;
}

var deletePressed = function(el) {
  //logg("deletePressed() " + el.attr("class"));
  el.val("");
  scrapeGlyph(el);
}

var arrowPressed = function(arr) {
  //logg("arrowPressed()");
  el = SQ;
  var prev = null;
  var goingDirBack = goingDir;
  switch (arr) {
    case KBD.UP:
      goingDir = DIR.LONG;
      prev = nextSquare(el.attr("class"), DIR.BACKWARD, false);
      break;
    case KBD.DOWN:
      goingDir = DIR.LONG;
      prev = nextSquare(el.attr("class"), DIR.FORWARD, false);
      break;
    case KBD.LEFT:
      goingDir = DIR.ACROSS;
      prev = nextSquare(el.attr("class"), DIR.BACKWARD, false);
      break;
    case KBD.RIGHT:
      goingDir = DIR.ACROSS;
      prev = nextSquare(el.attr("class"), DIR.FORWARD, false);
      break;
    default:
  }
  if (prev.is("input") == false) {
    goingDir = goingDirBack;
  } else {
    prev.focus();
  }
}

var spacePressed = function() {
  logg("spacePressed()");
  if (goingDir != DIR.ACROSS) {
    goingDir = DIR.ACROSS;

    if (nextSquare(SQ.attr("class"), DIR.FORWARD, false, true) == false) {
      goingDir = DIR.LONG;
    }
    displayDirection(goingDir);
    return;
  }
  if (goingDir != DIR.LONG) {
    goingDir = DIR.LONG;
    if (nextSquare(SQ.attr("class"), DIR.FORWARD, false, true) == false) {
      goingDir = DIR.ACROSS;
    }
    displayDirection(goingDir);
    return;
  }
}

var highlightCurrentElement = function() {

  logg("highlightCurrentElement");

  darkenElements();
  var el = SQ;

  while (el != null && el.is("input")) {
    el.parent().addClass("hlt");
    el = nextSquare(el.attr("class"), DIR.FORWARD, true);
  }

  el = SQ;

  while (el != null && el.is("input")) {
    el.parent().addClass("hlt");

    // update current question id
    // this must align with the direction properly

    if (el.hasClass("el_st")) {
      var cl = el.attr("class").split(" ");
      for (var i = 0; i < cl.length; i++) {
        var cl_bit = cl[i].split("_");
        if (cl_bit[0] == "qid") {
          logg("qid: " + cl_bit[1] + ", goingDir: " + goingDir + ", list id: " + $("#qu_desc_" + cl_bit[1]).parent().parent().attr("id"));
          if (goingDir == DIR.ACROSS &&
              $("#qu_desc_" + cl_bit[1]).parent().parent().attr("id") == "q_list_across"
            ||
              goingDir == DIR.LONG &&
              $("#qu_desc_" + cl_bit[1]).parent().parent().attr("id") == "q_list_down") {
            logg("qid: " + cl_bit[1]);
            QU_ID = cl_bit[1];
          } else {
            logg("qid doesn't conform with going direction");
          }
        }
      }
    }

    el = nextSquare(el.attr("class"), DIR.BACKWARD, true);
  }
}

var darkenElements = function() {
  if (KEEP_HIGHLIT_ELEMENT) {
    return;
  }
  ly_cont.find("span.hlt").removeClass("hlt");
}

var processSquare = function(el, e) {

  var code = e.which;

  if (((code >= 1040 && code <=1103) || (code == 1025 || code == 1105)/*yo*/)
      || ((code >= 65 && code <= 90) || (code >=97 && code <= 122))) {

    putGlyph(el, code);

    nextSquare(el.attr("class"), DIR.FORWARD, false).focus();
    return String.fromCharCode(code);
  }

  return "";
}

var putGlyph = function(el, code) {
  logg("putGlyph(" + el + ", " + code + ")");
  el.css("background",
      "transparent url('" +
      CONTEXT_PATH +
      "static/glyphs/" +
      IMAGE_GLYPHS[code] + "_0.png') " +
      "left top no-repeat");

  verifyDone();
}

var scrapeGlyph = function(el) {
  el.css("background", "none");
}

var preloadGlyphs = function() {
  logg("preloadGlyphs()");
  var images = new Array();
  for (var g in IMAGE_GLYPHS) {
    images.push(CONTEXT_PATH + "static/glyphs/" + IMAGE_GLYPHS[g] + "_0.png");
  }
  $.preloadImages(images);
}

var putGlyphs = function() {

  logg("putGlyphs()");

  ly_cont.find(".el").each(function() {
    var el = $(this);
    if (el.val().trim().length == 1) {
      var code = el.val().trim().charCodeAt(0);

      el.css("background",
        "transparent url('" +
        CONTEXT_PATH +
        "static/glyphs/" +
        IMAGE_GLYPHS[code] + "_0.png') " +
        "left top no-repeat");
    }
  });
}

/*var nextSquare = function(sq_class, fb) {
  return nextSquare(sq_class, fb, false);
}*/

var nextSquareCheck = function(sq_class, fb) {
  logg("nextSquareCheck()");
//  logg("going dir: " + goingDir);
  return nextSquare(sq_class, fb, true);
}

/**
 * Returns the next square depending on the direction of movement.
 *
 * Parameters:
 *
 * sq_class: the "class" parameter of the element we need next for. contains all
 *           the information to extract, such as element id, element square id,
 *           etc.
 *
 * fb direction: forward or backward
 *
 * assert: whether to search for next element with a turn, if the current el-t
 *         has ended (false) or just indicate whether the el-t has ended (true)
 *
 * check_crossing: checks whether this sq is a crossing.
 */
var nextSquare = function(sq_class, fb, assert,
    check_crossing) { // fb is direction: forward or backward

  logg("nextSquare(" + sq_class + ", " + fb + ", " + assert + ")");

  var bits = sq_class.split(" ");
  var sq_ids = new Array();
  var el_id_direction = 0; // id of the element of current direction

  for (var i = 0; i < bits.length; i++) {
    // first, we obtain all classes like el_A_B (1 for regular, 2 for crossing)
    // where A stands for element id, B stands for square number
    if (
        bits[i].substring(0, 3) == "el_" &&
        bits[i].split("_")[1] !== "st" &&
        bits[i].replace(/[^_]/g, "").length > 1) {
      sq_ids.push(bits[i]);
    }
    // di_* classes indicate element with which id has what direction.
    // knowing current direction, we can effectively find out whether
    // next square in that direction related to the element we're interested in
    if (bits[i].substring(0, 3) == "di_") {
      if (
          (bits[i].substring(0, 5) == "di_a_" && goingDir == DIR.ACROSS)
          ||
          (bits[i].substring(0, 5) == "di_l_" && goingDir == DIR.LONG)
      ) {
        el_id_direction = parseInt(bits[i].split("_")[2]);
      }
    }
  }

//  logg("el_id_direction: " + el_id_direction);

  if (sq_ids.length > 1) {

    logg("crossing");

    if (check_crossing == true) {
      return true;
    }

    // a crossing. lets find out the direction
    // we look at what direction was set before
    // got more than one el_X_Y
    // let us first check the first el_X_Y

    var id_ch = sq_ids[0].split("_"); // getting the first el_X_Y
    var el_id = parseInt(id_ch[1]);  // element id
    var sq_num = parseInt(id_ch[2]); // square number
    var next_id = fb == DIR.FORWARD ? (sq_num + 1) : (sq_num - 1);

    var next = ly_cont.find(".el_" + el_id + "_" + (next_id)).eq(0);

//    logg("checking 1: elid: " + el_id + ", sqnm: " + sq_num + ", nextid: " + next_id);
//    logg("next el: " + next.attr("class"));

    // check whether we've selected the correct el_X_Y:
    // if the goingDir and next's dir are the same, we're cool
    // otherwise, we need to check the second el_X_Y.
    //
    // this condition also performs the turn if the element has ended but theres
    // a possibility to take a turn. if next.hasClass() returns false (which is
    // true in case it's not an <input>), we're trying to turn, changin the
    // direction

//    logg("goingDir: " + goingDir + ", class: " + next.attr("class"));
    var supposedId = "di_" + (goingDir == DIR.ACROSS ? "a" : "l") + "_" + el_id_direction;

    //if ((goingDir == DIR.ACROSS && next.hasClass("d_acro"))
    //    || (goingDir == DIR.LONG && next.hasClass("d_long"))) {
    //  // yes, we're cool
    if (el_id == el_id_direction) {
      return next;
    } else {
      // now, let's check the second el_X_Y

      id_ch = sq_ids[1].split("_");  // getting the second el_X_Y
      el_id = id_ch[1];
      sq_num = id_ch[2];
      var next_id = fb == DIR.FORWARD ? (sq_num*1+1) : (sq_num*1-1);
      var next = ly_cont.find(".el_" + el_id + "_" + (next_id)).eq(0);

//      logg("checking 2: elid: " + el_id + ", sqnm: " + sq_num +
//          ", nextid: " + next_id);
//      logg("assert: " + assert + ", goingDir: " + goingDir + ", d_acro: "
//          + next.hasClass("d_acro") + ", d_long: " + next.hasClass("d_long"));

      if (assert && el_id != el_id_direction
          //(
          //  (goingDir == DIR.ACROSS && next.hasClass("d_acro") == false)
          //  ||
          //  (goingDir == DIR.LONG && next.hasClass("d_long") == false)
          //)
      ) {
        logg("ns: got null");
        return null;
      }
      logg("next el=" + next.attr("class"));
      return next;
    }

  } else {

    if (check_crossing == true) {
      return false;
    }

    // regular square
    var id_ch = sq_ids[0].split("_");
    var el_id = id_ch[1];
    var sq_num = id_ch[2];
    var next_id = fb == DIR.FORWARD ? (sq_num*1+1) : sq_num*1-1;
    var next = ly_cont.find(".el_" + el_id + "_" + next_id).eq(0);

    return next;
  }

  //return next;
}

/*var nextSquareDirection = function() {

}*/

var highlightSquare = function(el) {
  if (SQ != null) {
    SQ.parent().removeClass("hl");
  }
  el.parent().addClass("hl");
}

var highlightWord = function() {

}

var highlightGlueBtn = function() {
  logg("highlightGlueBtn()");
  logg("GS_COUNT: " + GS_COUNT);
  if (GS_COUNT <= 0) {
    GS_COUNT = 0;
    noMoreGlues();
    return;
  }
  var btn = $("#btn_glue");
  btn.addClass("hl").fadeTo(100, 1);
  initGlueBtn(btn);
}

var darkenGlueBtn = function() {
  logg("darkenGlueBtn()");
  var btn = $("#btn_glue");
  btn.removeClass("hl").fadeTo(100, .5);
  btn.unbind("mouseover").unbind("mouseout");
  //disableGlueBtn(btn); // ?
  hideGlueHint();
}

var highlightSawBtn = function() {
  logg("highlightSawBtn()");
  logg("GS_COUNT: " + GS_COUNT);
  if (GS_COUNT >= GS_MAX) {
    GS_COUNT = GS_MAX;
    noMoreSaws();
    return;
  }
  var btn = $("#btn_saw");
  btn.addClass("hl").fadeTo(100, 1);
  initSawBtn(btn);
}

var darkenSawBtn = function() {
  logg("darkenSawBtn()");
  var btn = $("#btn_saw");
  btn.removeClass("hl").fadeTo(100, .5);
  btn.unbind("mouseover").unbind("mouseout");
  //disableGlueBtn(btn); // ?
  hideSawHint();
}

var displayDirection = function(force) {

  logg("displayDirection: " + force);

  if (force == DIR.LONG) {
    $("#dir_btm").show();
    $("#dir_rgt").hide();
  } else if (force == DIR.ACROSS) {
    $("#dir_btm").hide();
    $("#dir_rgt").show();
  }

  highlightCurrentElement();
};

