/*
  minesweeper
*/

Array.prototype.include = function(_x) {
  for(var i=0; i<this.length; i++) {
    if (this[i]==_x) {return(true)}
  }
  return(false);
}

Array.prototype.delete_at = function(_x) {
  var newarray = new Array();
  for(var i=0; i<this.length; i++) {
    if (i!=_x) {newarray.push(this[i])}
  }
  return(newarray);
}

Array.prototype.compact = function() {
  var newarray = new Array();
  for(var i=0; i<this.length; i++) {
    if (!newarray.include(this[i])) {
      newarray.push(this[i]);
    }
  }
  return(newarray);
}

function Timer() {
  function start() {
    start_time = new Date();
  }
  
  function stop() {
    if (stop_time==null) {
      stop_time = new Date();
    }
  }
  
  function reset() {
    start_time = null;
    stop_time = null;
  }
  
  function sec() {
    var end_time = stop_time;
    if (start_time==null) {return(null);}
    if (end_time==null) {end_time = new Date();}
    var sec = (end_time.getTime() - start_time.getTime())/1000;
    return(sec);
  }
  
  //Timer#initialize
  var start_time = null;
  var stop_time = null;
  
  this.start = start;
  this.stop  = stop;
  this.reset = reset;
  this.sec = sec;
  return(this);
}


//Mine class
function Mine(x,y,mine) {
  
  //Mine::Table class
  function Table(x,y,mine) {
    //constants
    // cell status
    var CellStatus = new Array();
    CellStatus['closed']  = 0;
    CellStatus['flagged'] = 1;
    CellStatus['marked']  = 2;
    CellStatus['bomb']    = 3;
    CellStatus['bombed']  = 4;
    CellStatus['blank']   = 10;
    CellStatus['num1']    = 11;
    CellStatus['num2']    = 12;
    CellStatus['num3']    = 13;
    CellStatus['num4']    = 14;
    CellStatus['num5']    = 15;
    CellStatus['num6']    = 16;
    CellStatus['num7']    = 17;
    CellStatus['num8']    = 18;
    
    //Mine::Table::Cell class
    function Cell(cell_x,cell_y,status) {
      
      //Cell#functions
      function getStatusName(status) {
        for(var _x in CellStatus) {
          if (CellStatus[_x] != status) {continue;}
          return(_x);
        }
      }
      
      function getStatus() {
        var name = this.statusName;
        if (this.status!=CellStatus[this.statusName]) {
          name = getStatusName(this.status);
          this.statusName = name;
        }
        return(name);
      }
      
      function setStatus(name) {
        this.status = CellStatus[name];
        this.statusName  = name;
      }
      
      function check() {
        var s = this.status;
        if (s<=CellStatus['marked']){
          s = (s+1) % (CellStatus['marked']+1)
          this.setStatus(getStatusName(s));
        }
      }
      
      function open() {
        var result = new Array();
        if (this.status == CellStatus['flagged']){return(result)}
        var blank = CellStatus['blank'];
        if (this.body == CellStatus['bomb']) {
          result[0] = "bomb";
          this.setStatus('bombed');
        }else if(this.body>blank) {
          result[0] = "num";
          this.setStatus('num'+(this.body-blank));
        }else if(this.body==blank) {
          result[0] = "blank";
          result[1] = this.getAroundCells();
          this.setStatus('blank')
        }
        return(result);
      }
      
      function update(result) {
        var obj = document.getElementById(this.id);
        obj.className = this.statusName;
      }
      
      function getAroundCells() {
        var a_cells = new Array();
        var t_x = this.x;
        var t_y = this.y;
        for(var i=0; i<3 ;i++) {
          var e_y = t_y + i - 1;
          if (e_y<0 || e_y>=y){continue;}
          for(var j=0; j<3 ;j++) {
            var e_x = t_x + j - 1;
            if (e_x<0 || e_x>=x){continue;}
            if (i==1 && j==1){continue;}
            a_cells.push(e_y*x+e_x);
          }
        }
        return(a_cells);
      }
      
      //Cell#initialize
      status = status || CellStatus['closed'];
      this.x = cell_x;
      this.y = cell_y;
      this.num = cell_y * y + cell_x;
      var id = "cell_" + cell_y + "_" + cell_x;
      this.id     = id;
      this.status = status;
      this.statusName = getStatusName(status);
      this.getStatus = getStatus;
      this.setStatus = setStatus;
      this.body     = CellStatus['blank'];
      this.check  = check;
      this.open   = open;
      this.update = update;
      this.getAroundCells = getAroundCells;
      return(this);
    }
    
    //Table#functions
    // createCells
    function createCells(x,y,mine){
      var rows = new Array(y);
      var columns = new Array(y*x);
      var mine_cells = new Array();
      while(mine_cells.length<mine) {
        var r = Math.floor(Math.random()*x*y);
        if (!mine_cells.include(r)) {
          mine_cells.push(r);
        }
      }
      for(var i=0; i<y; i++){
        rows[i] = new Array(x);
        for(var j=0; j<x; j++){
          //var id = "cell_"+i+"_"+j;
          var status = CellStatus['closed'];
          var cell = new Cell(j,i,status);
          rows[i][j] = cell;
          columns[i*x+j] = cell;
        }
      }
      for(var i=0; i<mine_cells.length; i++) {
        var m = mine_cells[i];
        var m_cell = columns[m];
        m_cell.body = CellStatus['bomb'];
        var a_cells = m_cell.getAroundCells();
          for(var j=0; j<a_cells.length; j++) {
          var a_cell = columns[a_cells[j]];
          //if (!a_cell) {alert("illegal_cell:"+a_cells);}
          if (a_cell.body>=CellStatus['blank']){
            columns[a_cells[j]].body += 1;
          }
        }
      }
      return([rows,columns]);
    }
    
    // checkCell
    function checkCell(cell_num) {
      var cell = this.cols[cell_num];
      cell.check();
      cell.update();
    }
    
    // openCell
    function openCell(cell_num) {
      var cell =this.cols[cell_num];
      var r = cell.open();
      cell.update();
      // open around cells
      if (r[0]=="blank") {
        var arounds = r[1];
        while(arounds.length>0) {
          var arounds_plus = new Array();
          for(var i=0; i<arounds.length; i++) {
            var a_cell = this.cols[arounds[i]];
            if (a_cell.status == CellStatus['closed']) {
              var _r = a_cell.open();
              if (_r[0]=="blank") {
                for (var j=0; j<_r[1].length; j++) {
                  if (arounds_plus.include(_r[1][j])){continue;}
                  arounds_plus.push(_r[1][j])
                }
              }
              a_cell.update();
            }
            arounds = arounds.delete_at(i);
          }
          arounds = arounds.concat(arounds_plus);
        }
      }
    }
    
    // clickCell
    function clickCell(e,i,j,menu) {
      setTime();
      var cell_num = i*x + j;
      e.cancelBubble = true;
      if (document.all){e=window.event}
      if (e.altKey||e.shiftKey||e.ctrlKey||menu) {
        this.checkCell(cell_num);
      }else{
        this.openCell(cell_num);
      }
      this.check();
      return(false);
    }
    
    // check game
    function check() {
      var game_over   = false;
      var mine_size   = 0;
      var opened_size = 0;
      for(var i=0; i<this.cols.length; i++) {
        var _cell = this.cols[i];
        var status = _cell.status;
        if (status == CellStatus['bombed']) {
          game_over = true;
          break;
        }
        if (status == CellStatus['flagged']){mine_size+=1;}
        if (status >= CellStatus['blank']){opened_size+=1;}
      }
      var left_mine = this.mine - mine_size;
      //if (left_mine < 0 ) {left_mine = 0}
      document.getElementById('left_mine').innerHTML = left_mine;
      if (game_over) {
        //gameover
        setTime(true);
        document.getElementById('facemark').innerHTML = "(ßAß)";
        alert('Gameover');
      }else if (opened_size==x*y-mine_size) {
        //clear!
        setTime(true);
        document.getElementById('facemark').innerHTML = "(ßÍß)";
        alert('Congratulation!!');
      }
    }
    
    function setTime(stop) {
      if (timer.sec()==null) {
        timer.start();
      }
      if (stop) {timer.stop()}
      updateTime();
    }
    
    function updateTime() {
      var sec = Math.floor(timer.sec());
      document.getElementById('time').innerHTML = sec;
    }
    
    function debug(str) {
      var deb = document.getElementById('debug');
      deb.value += str;
    }
    
    //Table#initialize
    var cells = createCells(x,y,mine);
    var timer = new Timer();
    
    this.x = x;
    this.y = y;
    this.width  = x;
    this.height = y;
    this.mine   = mine;
    this.rows = cells[0];
    this.cols = cells[1];
    this.clickCell = clickCell;
    this.checkCell = checkCell;
    this.openCell  = openCell;
    this.check     = check;
    this.updateTime = updateTime;
    return(this);
  }
  
  //Mine#functions
  // makeTableTag
  function makeTableTag(table_name) {
    var table = this.table;
    if (!table_name) {table_name = "tablename";}
    eval(table_name + "= this.table;");
    out = "<table summary='mine_"+table_name+"' border='1'>";
    var span = Math.floor(table.width/3);
    var span_ = span + table.width - span*3;
    out += "<tr><td colspan='"+table.width+"' class='title' id='title'>mine sweeper</td></tr>";
    out += "<tr>\n";
    out += "  <td colspan='"+span+"' class='left_mine'>[<span id='left_mine'>"+table.mine+"</span>]</td>\n";
    out += "  <td colspan='"+span+"' class='facemark' id='facemark' onclick='location.reload();'>(^^)</td>\n";
    out += "  <td colspan='"+span_+"' class='time' id='time'> </td>\n";
    out += "</tr>\n";
    for(var y=0; y<table.height; y++){
      out += "<tr id='columns'>\n";
      for(var x=0; x<table.width; x++){
        col = table.rows[y][x];
        out += "<td class='col' onclick='return("+table_name+".clickCell(event,"+y+","+x+"))' oncontextmenu='return("+table_name+".clickCell(event,"+y+","+x+",true))'>";
        out += "<div id='"+col.id+"' class='"+col.getStatus()+"'>&nbsp;</div>";
        out += "</td>\n";
      }
      out += "</tr>\n";
    }
    out += "</table>";
    out += "<script type='text/javascript'>setInterval('"+table_name+".updateTime()',200);</script>"
    return(out);
  }
  
  //Mine#initialize
  if (!x) {x="Low";}
  switch (x) {
    case "Low":
      x=9;y=9;mine=10;
      break;
    case "Middle":
      x=16;y=16;mine=40;
      break;
    case "High":
      x=30;y=16;mine=99;
      break;
    default:
      x=x||9;y=y||9;mine=mine||10;
  }
  
  this.x = x;
  this.y = y;
  this.width  = x;
  this.height = y;
  this.mine   = mine;
  table = new Table(x,y,mine);
  this.table = table;
  this.makeTableTag = makeTableTag;
  return(this);
}
