// constructorizer editor
// Copyright 2010 Constructorizer (Travis Glines)
// Created by Travis Glines Summer of 2010

function Constructorizer(pre_object,editor,extra_height,theme_object){
  prettyPrint();
  this.canvas = document.getElementById("canvas");
  this.context = this.canvas.getContext("2d");
  this.editor = editor;
  this.extra_height = extra_height;
  this.pre_object = pre_object;
  this.theme_object = theme_object;

  this.addExtraHeight = function(added_height){
    this.extra_height += added_height;
    this.resetValues();
    this.drawLoop();
  }

  this.plainContent = function(){
    var plain_content = "";
    this.pre_object.children().each(function(){
      plain_content+=$(this).html();
    });
    plain_content = this.cleanHTMLSpecialChars(plain_content);
    return plain_content;
  }

  this.cleanHTMLSpecialChars = function (dirty_string){
    dirty_string = dirty_string.replace(/&lt;/g , "<");
    dirty_string = dirty_string.replace(/&gt;/g , ">");
    dirty_string = dirty_string.replace(/&nbsp;/g , " ");
    dirty_string = dirty_string.replace(/<br>/g , "\n");
    return dirty_string;
  }

  this.plain_content=this.plainContent();

  this.setDefaults = function(){
    this.canvas.width = this.editor.width();
    this.canvas.height = this.editor.height();
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    this.background_color = this.theme_object["bg_c"];
    this.line_highlighter_color = this.theme_object["bg_lh_c"];
    this.font_size = this.theme_object["font_size"];
    this.context.font = this.theme_object["font"];
    this.sidebar_color = this.theme_object["sb_c"];
    this.sidebar_text_color = this.theme_object["sb_t_c"];
    this.sidebar_width = this.context.measureText(this.getNumLines()+' ').width;
    this.text_indent = this.sidebar_width+5;
    this.top_spacing = 5;		
    this.cursor_color = this.theme_object["c_c"];
    this.cursor_position = 14;
    this.cursor_blink_count = 0;
    this.context.textBaseline = "top";
    this.shift_down = false;
    this.has_focus = false;
  }

  this.resetTheme = function(){
    this.background_color = this.theme_object["bg_c"];
    this.line_highlighter_color = this.theme_object["bg_lh_c"];
    this.font_size = this.theme_object["font_size"];
    this.context.font = this.theme_object["font"];
    this.sidebar_color = this.theme_object["sb_c"];
    this.sidebar_text_color = this.theme_object["sb_t_c"];
    this.cursor_color = this.theme_object["c_c"];
  }
		
  this.setBackgroundColor = function(color){
    this.background_color = color;
  }

  this.resetValues = function(){
    this.canvas.width = this.editor.width();
    this.canvas.height = this.editor.height();
    $("section#content").height(this.canvas.height+this.extra_height);
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    this.context.font = this.theme_object["font"];
    this.context.textBaseline = "top";
    this.sidebar_width = this.context.measureText(this.getNumLines()+' ').width;
    this.text_indent = this.sidebar_width+5;
  }
		
  this.getNumLines = function(){
    return this.plain_content.split("\n").length;
  }
		
  this.getHeightPerLine = function(){
    return this.font_size+2;
  }
		
  this.getLengthOfLongestLine = function(){
    var lines = this.plain_content.split("\n");
    var largest = 0;
    for(var i=0;i<lines.length;i++){
      var line_length = this.context.measureText(lines[i]).width+this.text_indent;
      if( line_length > largest){
        largest=line_length;
      }
    }
    return largest;
  }

  this.getLineNumber = function(){
    var textLeft = this.plain_content.substring(0,this.cursor_position);
    var lineNumber = textLeft.split("\n").length-1;
    return lineNumber;
  }
		
  this.setDefaults();		
		
  this.drawSpanText = function(span_object,prev_text,i){
    if(span_object.attr('class')=="str"){this.context.fillStyle=this.theme_object["str_c"];}
    else if(span_object.attr('class')=="kwd"){this.context.fillStyle=this.theme_object["kwd_c"];}
    else if(span_object.attr('class')=="com"){this.context.fillStyle=this.theme_object["com_c"];}
    else if(span_object.attr('class')=="typ"){this.context.fillStyle=this.theme_object["typ_c"];}
    else if(span_object.attr('class')=="lit"){this.context.fillStyle=this.theme_object["lit_c"];}
    else if(span_object.attr('class')=="pln"){this.context.fillStyle=this.theme_object["pln_c"];}
    else if(span_object.attr('class')=="pun"){this.context.fillStyle=this.theme_object["pun_c"];}
    else if(span_object.attr('class')=="tag"){this.context.fillStyle=this.theme_object["tag_c"];}
    else if(span_object.attr('class')=="atn"){this.context.fillStyle=this.theme_object["atn_c"];}
    else if(span_object.attr('class')=="atv"){this.context.fillStyle=this.theme_object["atv_c"];}
    else if(span_object.attr('class')=="dec"){this.context.fillStyle=this.theme_object["dec_c"];}
    else{this.context.fillStyle=this.theme_object["pln_c"];}
    this.context.fillText(span_object.text(), this.text_indent+this.context.measureText(prev_text).width, this.top_spacing+this.getHeightPerLine()*i);
    return span_object.text();
  }

  this.drawLineText = function(lineText,i,max,last_span){
    if(i!=0){
      lineText = last_span+lineText;
    }
    if(i!=max-1){
      last_span = lineText.substr(lineText.lastIndexOf("<"),lineText.lastIndexOf(">")-lineText.lastIndexOf("<")+1);
    }
    lineText = lineText+last_span;
    lineText = '<pre>'+lineText+'</pre>';
    var $lineText_object = $(lineText);
    var prev_text = "";
    $lineText_object.filter('pre').children().each(function(){
      prev_text+=constructorizer.drawSpanText($(this),prev_text,i);
    });

    if(i!=max-1){
      return last_span;
    }
    else{
      return "";
    }
  }

  this.drawLoop = function(){
    //Reset Values in case user changed window size
    this.resetValues();
    //Draw Background
    this.context.fillStyle = this.background_color;
    this.context.fillRect(0, 0, this.width, this.height);
    //Draw Line Highligher
    if(this.has_focus){
      this.context.fillStyle = this.line_highlighter_color
      this.context.fillRect(this.sidebar_width,this.getLineNumber()*this.getHeightPerLine()+this.top_spacing,this.width-this.sidebar_width,this.getHeightPerLine());		  
    }
    //Draw Text
    var contentSplitted = this.pre_object.html().split("<br>");
    var last_span = "";
    for(var i=0;i<contentSplitted.length;i++){
      last_span = this.drawLineText(contentSplitted[i],i,contentSplitted.length,last_span);
    }
    //Draw Sidebar
    this.context.fillStyle = this.sidebar_color;
    this.context.fillRect(0, 0, this.sidebar_width, this.height);
    this.context.fillStyle = this.sidebar_text_color;
    for(var j=0;j<this.getNumLines();j++){
      this.context.fillText(j+1, 3, this.top_spacing+this.getHeightPerLine()*j);
    }          
    //If Text Is Too Short set min height else fit height
    if( (this.top_spacing + this.getNumLines()*this.getHeightPerLine()) < 200){
      this.editor.height(200);
    }
    else{
      this.editor.height(this.top_spacing + this.getNumLines()*this.getHeightPerLine());
    }
    if(this.editor.width()<1000){
      this.editor.width(1000);
    }
    else if(this.getLengthOfLongestLine() <980){
      this.editor.width(1000);
    }
    else{
      this.editor.width( this.getLengthOfLongestLine()+20 );
    }
    //Draw Cursor
    if(this.cursor_blink_count<6 && this.has_focus){
      this.context.fillStyle = this.cursor_color;
      var textLeft = this.plain_content.substring(0,this.cursor_position);
      var lineNumber = textLeft.split("\n").length-1;
      var lineTextLeft = textLeft.split("\n")[lineNumber];
      var cursorOffsetX = this.context.measureText(lineTextLeft).width;
      this.context.fillRect(cursorOffsetX+this.text_indent,this.top_spacing+lineNumber*this.getHeightPerLine(),1,this.font_size);
    }
    if(this.cursor_blink_count>12){
      this.cursor_blink_count=0;
    }
    this.cursor_blink_count++;
  }

  this.startDrawLoop = function(){
    //Draw every 1/2 second
    this.interval_id = setInterval("constructorizer.drawLoop()",100);
  }

  this.stopDrawLoop = function(){
    clearInterval ( this.interval_id );
  }

  this.keyDown = function(keyCode,e){
    // Left Key
    if(keyCode==37){
      this.cursor_blink_count=0;
      if(this.cursor_position>0){
        this.cursor_position--;
      }
    }
    // Right Key
    else if(keyCode==39){
      this.cursor_blink_count=0;
      if(this.cursor_position<this.plain_content.length){
        this.cursor_position++;
      }
    }
    // Up Key
    else if(keyCode==38){
      this.cursor_blink_count=0;
      var textLeft = this.plain_content.substring(0,this.cursor_position);
      var lineNumber = textLeft.split("\n").length-1;
      if(lineNumber>0){
        var lineTextLeft = textLeft.split("\n")[lineNumber];
        var prevlineText = textLeft.split("\n")[lineNumber-1];
        var prevlineTextRightLength = prevlineText.length - lineTextLeft.length;
        if(prevlineTextRightLength>0){
          this.cursor_position = this.cursor_position - lineTextLeft.length - prevlineTextRightLength-1;
        }
        else{
          this.cursor_position = this.cursor_position - lineTextLeft.length-1;
        }
      }
    }
    // Down Key
    else if(keyCode==40){
      this.cursor_blink_count=0;
      var textLeft = this.plain_content.substring(0,this.cursor_position);
      var textRight = this.plain_content.substring(this.cursor_position);
      var lineNumber = textLeft.split("\n").length-1;
      if(lineNumber<this.getNumLines()-1){
        var lineTextLeft = textLeft.split("\n")[lineNumber];
        var lineTextRight = textRight.split("\n")[0];
        var nextlineText = textRight.split("\n")[1];
        var nextlineTextRightLength = nextlineText.length - lineTextLeft.length;
        if(nextlineTextRightLength>0){
          this.cursor_position = this.cursor_position + lineTextRight.length + lineTextLeft.length+1;
        }
        else{
          this.cursor_position = this.cursor_position + lineTextRight.length + nextlineText.length+1;
        }
      }
    }
    // Return
    else if(keyCode==13){
      this.addCharacter("\n");
    }
    // Spacebar
    else if(keyCode==32){
      this.addCharacter(" ");
    }
    // Backspace
    else if(keyCode==8){
      this.removeCharacter(this.cursor_position,-1);
    }
    // Delete
    else if(keyCode==46){
      this.removeCharacter(this.cursor_position+1,0);
    }
    // Shift Key
    else if(keyCode==16){
      this.shift_down = true;
    }
    // Letters
    else if (keyCode>=65 && keyCode <= 90){
      var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
      var letterNumber = keyCode-65;
      if(this.shift_down){
        this.addCharacter(letters[letterNumber].toUpperCase());
      }
      else{
        this.addCharacter(letters[letterNumber]);
      }
    }
    // Numbers
    else if (keyCode>=48 && keyCode <= 57){
      var numbers = ["0","1","2","3","4","5","6","7","8","9"];
      var numbers_alt = [")","!","@","#","$","%","^","&","*","("];
      var numberNumber = keyCode-48;
      if(this.shift_down){
        this.addCharacter(numbers_alt[numberNumber]);
      }
      else{
        this.addCharacter(numbers[numberNumber]);
      }
    }
    // Tilde Key
    else if(keyCode==192){
      if(this.shift_down){
        this.addCharacter("~");
      }
      else{
        this.addCharacter("`");
      }
    }
    // Hyphen Key
    else if(keyCode==109||keyCode==189){
      if(this.shift_down){
        this.addCharacter("_");
      }
      else{
        this.addCharacter("-");
      }
    }
    // Equals Key
    else if(keyCode==187||keyCode==107){
      if(this.shift_down){
        this.addCharacter("+");
      }
      else{
        this.addCharacter("=");
      }
    }
    // Left Bracket Key
    else if(keyCode==219){
      if(this.shift_down){
        this.addCharacter("{");
      }
      else{
        this.addCharacter("[");
      }
    }
    // Right Bracket Key
    else if(keyCode==221){
      if(this.shift_down){
        this.addCharacter("}");
      }
      else{
        this.addCharacter("]");
      }
    }
    // Forwardslash Key
    else if(keyCode==220){
      if(this.shift_down){
        this.addCharacter("|");
      }
      else{
        this.addCharacter("\\");
      }
    }
    // Colon Key
    else if(keyCode==186||keyCode==59){
      if(this.shift_down){
        this.addCharacter(":");
      }
      else{
        this.addCharacter(";");
      }
    }
    // Quote Key
    else if(keyCode==222){
      if(this.shift_down){
        this.addCharacter('"');
      }
      else{
        this.addCharacter("'");
      }
    }
    // Less Than Key
    else if(keyCode==188){
      if(this.shift_down){
        this.addCharacter('<');
      }
      else{
        this.addCharacter(",");
      }
    }
    // Greater Than Key
    else if(keyCode==190){
      if(this.shift_down){
        this.addCharacter('>');
      }
      else{
        this.addCharacter(".");
      }
    }
    // Question Mark Key
    else if(keyCode==191){
      if(this.shift_down){
        this.addCharacter('?');
      }
      else{
        this.addCharacter("/");
      }
    }
  }

  this.leftClick = function(x,y){
    this.cursor_blink_count=0;
    var lineNumber = Math.floor( (y-this.top_spacing)/(this.font_size+2) );
    this.setCursorLineNumber(lineNumber,x-this.text_indent);
  }

  this.setCursorLineNumber = function(lineNumber,x){
    if(lineNumber >= this.getNumLines()){
      this.cursor_position = this.plain_content.length;
    }
    else if(lineNumber<0){
      this.cursor_position = 0;
    }
    else{
      var start = 0;
      var tmp_index = 0;
      for(var i=0;i<lineNumber;i++){
        tmp_index = this.plain_content.indexOf("\n", start)
        start=tmp_index+1;
      }
      var lineTextLeft = "";
      var lineTextRight = this.getLineText(lineNumber);
      if(x>this.context.measureText(lineTextRight).width){
        this.cursor_position = start+lineTextRight.length;
      }
      else{
        for(var j=0; x>this.context.measureText(lineTextLeft).width;j++){
          lineTextLeft += lineTextRight[0];
          lineTextRight = lineTextRight.substring(1);
        }
        this.cursor_position = start+lineTextLeft.length-1;
      }
    }
  }

  this.getLineText = function(lineNumber){
    return this.plain_content.split("\n")[lineNumber];
  }

  this.keyUp = function(keyCode,e){
    if(keyCode == 16){
      this.shift_down = false;
    }
  }

  this.removeCharacter = function(position,cursorChange){
    this.cursor_blink_count=0;
    if(position>0){
      this.setPlainContent( this.plain_content.substring(0,position-1)+this.plain_content.substring(position) );
      this.cursor_position+=cursorChange;
    }
  }

  this.addCharacter = function(letter){
    this.cursor_blink_count=0;
    var new_string = this.plain_content.substring(0,this.cursor_position)+letter+this.plain_content.substring(this.cursor_position);
    this.setPlainContent( new_string );
    this.cursor_position++;
  }

  this.setPlainContent = function(plain_string){
    if(plain_string.match(/(\n*)$/)[0].length == 1){
      plain_string = plain_string+" ";
    }
    //console.log(plain_string);
    pre_object.html(this.addHTMLSpecialChars(plain_string));
    //console.log(pre_object.html());
    prettyPrint();
    //console.log(pre_object.html());
    this.plain_content=this.plainContent();
  }

  this.addHTMLSpecialChars = function (clean_string){
    clean_string = clean_string.replace(/</g , "&lt;");
    clean_string = clean_string.replace(/>/g , "&gt;");
    //clean_string = clean_string.replace(/ /g , "&nbsp;");
    return clean_string;
  }


  this.startDrawLoop();
}


