<!DOCTYPE html>
<html>

  <head>
    <script data-require="react@15.2.0" data-semver="15.2.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
    <script data-require="react@15.2.0" data-semver="15.2.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react-dom.min.js"></script>
    <link data-require="bootstrap-css@3.3.6" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css" />
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <div id="container" class="container"></div>
    <script src="script.js"></script>
  </body>

</html>
const Tile = (props) => {

  let className = "valid-";
  let isGameEnded = props.isGameEnded;
  let isValid = props.isValid;
  className += (isValid && !isGameEnded) ? "true" : "false";
  className += " col-xs-3";
  let num = null;
  if(props.number > 0) {
    num = <div className="number">{props.number}</div>
  }
  
  return (
    <div className={className} onClick={props.onClick.bind(props.game, props.number)}>{num}</div>
  )
};

const TilesFrame = (props) => {
  let tiles = [];
  
  let validIndexes = props.validIndexes;
  for (let i = 0; i < props.randomNumbers.length; i++) {
    let rNumber = props.randomNumbers[i];
    tiles.push(<Tile game={props.game} onClick={props.tileClicked} isGameEnded={props.isGameEnded} isValid={validIndexes.indexOf(i) != -1} number={rNumber}/>)
  }
 
  return (
    <div id="tiles-frame">
      {tiles}
    </div>
  )
}

const DoneFrame = (props) => {
  return (
    <div id = "done-frame" className = "well text-center">
      <h2>{props.resultMessage}</h2>
      <button className="btn btn-default" onClick={props.resetGame}>
        Play again
      </button>
    </div>
  )
}

const SliderFrame = (props) => {
  let gameLevel = props.gameLevel;
  let currentLevel = props.level;
  return (
    <div id = "slider-frame">
      Level
      <input type="range" 
             min="5"
             max="40"
             step="2"
             value={currentLevel}
             onChange={gameLevel}
       />
    </div>
  )
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    
    this.randomNumbers.bind(this);
    this.tileClicked.bind(this);
    this.checkValidClick.bind(this);
    this.getAdjacentIndexes.bind(this);
    this.updateDoneStatus.bind(this);
    this.gameLevel.bind(this);
    
    this.state = {
      randomNumbers : this.randomNumbers(4, 25),
      resultMessage : "",
      size: 4,
      level: 25
    };
  }
  
  resetGame () {
    var currentLevel = this.state.level;
    this.setState({
      randomNumbers : this.randomNumbers(4, currentLevel),
      resultMessage : "",
      size: 4,
      level: currentLevel
    });
  }
  
  randomNumbers (size, level) {
    var len = size*size;
    var rnArray = Array.apply(null, {length: len}).map(Number.call, Number);
    console.log(level)
    for (var i = 0; i < level; i++) {
      var index1 = Math.floor(Math.random() * len);
      var index2 = Math.floor(Math.random() * len);
      
      rnArray[index2] = [rnArray[index1], rnArray[index1] = rnArray[index2]][0];
    }
    
    return rnArray;
  }
  
  tileClicked (clickedTile) {
    if (this.state.resultMessage) {
      return;
    }
    var randomNumbers = this.state.randomNumbers;
    
    var clickedIndex = randomNumbers.indexOf(clickedTile);
    if (clickedIndex === -1) {
      throw new Error("Unexpected: Invalid Index");
    }
    var adjacentIndexes = this.getAdjacentIndexes(clickedIndex, 4);
    var zeroIndex = this.checkValidClick(clickedIndex, adjacentIndexes);
    if (zeroIndex === -1) {
      return;
    }
    
    randomNumbers[zeroIndex] = [randomNumbers[clickedIndex], randomNumbers[clickedIndex] = randomNumbers[zeroIndex]][0];
    this.setState({
      randomNumbers: randomNumbers
    });
    
    this.updateDoneStatus();
  }
  
  checkValidClick (clickedIndex, indexes) {
    var randomNumbers = this.state.randomNumbers;
    
    for (var i = 0; i < indexes.length; i++) {
      if (randomNumbers[indexes[i]] === 0) {
        return indexes[i];
      }
    }
    
    return -1;
  }
  
  getAdjacentIndexes (index, size) {
    var adjacentElements = [];
    
    var leftIndex = index - 1;
    var rightIndex = index + 1;
    var upIndex = index - size;
    var downIndex = index + size;
    
    if (leftIndex > -1 && (leftIndex % size) != (size - 1)) {
      adjacentElements.push(leftIndex);
    }
    
    if (rightIndex < (size*size) && (rightIndex % size) != 0) {
      adjacentElements.push(rightIndex);
    }
    
    if (upIndex > -1) {
      adjacentElements.push(upIndex);
    }
    
    if (downIndex < (size*size)) {
      adjacentElements.push(downIndex);
    }
    
    return adjacentElements;
  }
  
  updateDoneStatus () {
    var randomNumbers = this.state.randomNumbers;
    var success = true;
    for (var i = 0, j = i + 1; j < randomNumbers.length - 1; ++i, ++j) {
      if (randomNumbers[i] > randomNumbers[j]) {
        success = false;
        break;
      }
    }
    
    if (success) {
      this.setState({
        resultMessage: "Done!!!"
      });
    }
  }
  
  gameLevel(e) {
    this.setState({
      level: e.currentTarget.valueAsNumber
    });
  }
  
  render () {
    var randomNumbers = this.state.randomNumbers;
    var resultMessage = this.state.resultMessage;
    var size = this.state.size;
    var level = this.state.level;
    
    var success = false;
    if (resultMessage) {
      success = true;
    }
    
    return (
      <div id="game">
        <h1>Tiles!!!</h1>
        <div> Move the blank tile to the top left or bottom right corner
        with all the tiles in asending order.</div>
        <div className="clearfix">
          <TilesFrame randomNumbers={randomNumbers}
                      tileClicked={this.tileClicked}
                      game={this}
                      validIndexes={this.getAdjacentIndexes(randomNumbers.indexOf(0), size)}
                      isGameEnded={success}
                      />
          <SliderFrame gameLevel={this.gameLevel}
                       level = {level}/>
          <DoneFrame resultMessage={resultMessage}
                     resetGame={this.resetGame.bind(this)}
                     />
        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <Game />,
  document.getElementById("container"));


body {
  font-family: 'Open Sans', sans-serif;
  -webkit-user-select: none; 
  -moz-user-select: none;     
  -ms-user-select: none;      
  user-select: none;
  width: 60%;
  min-width: 400px;
}

#tiles-frame {
  float:left;
  padding:10px;
  width: 60%;
  display:block;
  overflow:auto;
}

.col-xs-3 {
  background-color: #BBBBBB;
  border: 2px solid white;
  padding-bottom: 20%;
  position: relative;
  text-align: center;
}

.number {
  position:absolute;
  z-index: 1;
  background-color: #888888;
  width: 25px;
  height: 25px;
  line-height: 25px;
  top:35%;
  left: 35%;
  border-radius: 50%;
}

.col-xs-3:hover {
  background-color: #333333;
}

.col-xs-3.valid-true:active {
  background-color: #EEEEEE;
}

#done-frame {
  float: left;
  width:60%;
}

.valid-false {
  cursor: not-allowed;
}

.valid-true {
  cursor: pointer;
}

#slider-frame {
  width: 20%;
  text-align: center;
  float: left;
}

input[type=range]
{
    writing-mode: bt-lr; /* IE */
    -webkit-appearance: slider-vertical; /* WebKit */
    -moz-appearance: slider-vertical; /* WebKit */
    width: 8px;
    margin-top: 30px;
    margin-left: 50%;
}