var GenMapServer = "/cgi-bin/sitemapper/genmap.cgi";
var RedrawServer = "/cgi-bin/sitemapper/redraw.cgi";
var imageServer = "/cgi-bin/sitemapper/imagemap.cgi";
var RunningSearches = new Array(1); //collection of current calls
var SearchFailed = "Request Failed! Sorry please try again later..";
var storedGraph = "";
var lastTarget = "";
var imageID = "image";
var mapID = "sitemap";
var cmdParam = "cmd";
var VertexNameArray = null;
//enum for title type
var PageTitle = 0;
var maxTitleDisp = 16;

//load up colour options
function onLoad()
{
  loadColourNames( document.mapgenerator.nodeStrokeCol, "darkgreen" );

  loadColourNames( document.mapgenerator.nodeFillCol, "lightgreen" );

  loadColourNames( document.mapgenerator.fontCol, "black" );

  loadColourNames( document.mapgenerator.linkCol, "darkolivegreen" );
}

//why isn't this part of the object?
function clearCombo( combo )
{
  for (var i=combo.options.length-1; i>=0; i--)
  {
    combo.options[i] = null;
  }
  combo.selectedIndex = -1;
}

function displayEditOption( display )
{
  var nodeEditDiv = document.getElementById( "Edit_title" );

  if( display )
  {
		nodeEditDiv.style.display ='block';
	}
	else
  {
    nodeEditDiv.style.display ='none';
	}
}

//show a square of correct colour...
function displayColour( combo, colourCellID )
{
  var selopt = combo.options[combo.selectedIndex];
  var col = selopt.text;

  var coldiv = document.getElementById( colourCellID );
  coldiv.style.backgroundColor = col;
}

function selectColour( combobox, colourName )
{
  for( var i in combobox.options )
  {
    var opt = combobox.options[ i ];
    if( opt.text == colourName )
    {
      opt.selected = true;
      break;
    }
  }
}

function loadColourNames( combobox, selectCol )
{
  var colors = Color.namedColors();
  for (var k in colors)
  {
    var colvalue = colors[k];
    var opt = OPTION( null );
    opt.text = k;
    opt.value = colvalue;

    combobox.options.add( opt );

    if( opt.text == selectCol )
    {
      opt.selected = true;
    }
  }
  //make sure selected colour is displayed
  displayColour( combobox, combobox.name + "_disp" );
}

//show/hide a div
function toggleDiv( id )
{
  var div = document.getElementById(id);

  if(div.style.display =='block')
  {
		div.style.display='none';
	}
	else
  {
		div.style.display='block';
	}

}

function VisibleDiv( id, visible )
{
  var div = document.getElementById(id);

  if( visible )
  {
    div.style.display='block';
	}
	else
  {
		div.style.display='none';
	}
}

//generate Query Param Map for graph details
function CreateGraphParamMap()
{
  var rootSite = document.mapgenerator.rootSite.value;

  return { "site" : rootSite, "nodetitle" : GetNodeTitleParam(), "shape" : GetNodeShape(),
      "nodestrokecol" : GetNodeStrokeCol(), "fontcol" : GetFontCol(), "nodefillcol" : GetNodeFillCol(),
      "nodestyle" : GetNodeStyle(), "edgestyle" : GetEdgeStyle(), "edgecol" : GetEdgeCol(), 'direction' : GetGraphDir() };
}

//working around more IE bugs
var NodeTitleParam = PageTitle;

function clickedNodeTitle( value )
{
  NodeTitleParam = value;
}

//get label type to display in nodes
function GetNodeTitleParam()
{
  return NodeTitleParam;
}

function GetNodeShape()
{
  var combobox = document.mapgenerator.nodeshape;
  var sel = combobox.options[combobox.selectedIndex].value;
  return sel;
}

function ParseColour( colstring )
{
  //remove #
  return colstring.substring( 1, colstring.length );
}

function GetNodeStrokeCol()
{
  var combobox = document.mapgenerator.nodeStrokeCol;
  var sel = combobox.options[combobox.selectedIndex].value;
  return ParseColour( sel );
}

function GetNodeFillCol()
{
  var combobox = document.mapgenerator.nodeFillCol;
  var sel = combobox.options[combobox.selectedIndex].value;
  return ParseColour( sel );
}

function GetFontCol()
{
  var combobox = document.mapgenerator.fontCol;
  var sel = combobox.options[combobox.selectedIndex].value;
  return ParseColour( sel );
}

function GetNodeStyle()
{
  return "filled";
}

function GetEdgeCol()
{
  var combobox = document.mapgenerator.linkCol;
  var sel = combobox.options[combobox.selectedIndex].value;
  return ParseColour( sel );
}

function GetEdgeStyle()
{
  var combobox = document.mapgenerator.linkstyle;
  var sel = combobox.options[combobox.selectedIndex].value;
  return sel;
}

//working around IE bugs
var optGraphDir = 1;

function clickedGraphDir( value )
{
  optGraphDir = value;
}

function GetGraphDir()
{
  return optGraphDir;
}

//set the status
function setStatus( text )
{
  var x=document.getElementById("TopTable").rows[ 1 ].cells;
  x[0].innerHTML="<H3>" + text + "</H3>";
}

//use clicked map site
function MapSite()
{
  //check something in both text boxes
  if(document.mapgenerator.rootSite.value != '' && RunningSearches[ 0 ] == null )
  {
    if( storedGraph == "" || lastTarget != document.mapgenerator.rootSite.value )
    {
      lastTarget = document.mapgenerator.rootSite.value;
      DoMap(); //scan and draw
    }
    else
    {
      Redraw(); //just redraw
    }
  }
}

function setHaveStoredGraph( graphData )
{
  storedGraph = graphData;
}

//cope with different base url's mucking up security
  //returns with trailing slash, so can append things to it
function getBaseURL()
{
  var baseurl = location.href;
  //base url must end with .com
  var comStart = baseurl.indexOf( ".com" );
  var baseurlStart;
  if( comStart != -1 )
  {
    baseurlStart = baseurl.substring( 0, comStart + 5 );
  }
  else
  {
    //find 3rd slash
    var slashcount = 0;
    var index = 0;
    while( slashcount !=3 && index < baseurl.length )
    {
      if( baseurl.charAt( index ) == "/" )
      {
        slashcount = slashcount + 1;
      }
      
      index = index + 1;
      
    }
    
    baseurlStart = baseurl.substring( 0, index );
  }
  return baseurlStart;
}



//do a Ajax post as graph data may be rather large 10's kb
function makePOSTRequest(url, parameters)
{
  var http_request = MochiKit.Async.getXMLHttpRequest();

  http_request.open('POST', url, true );
  http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  http_request.onreadystatechange = PostProcessMap;
  http_request.send(parameters);

  RunningSearches[ 0 ] = http_request;
}

function GetNameIndexFromCombo( combo )
{
  var selopt = combo.options[combo.selectedIndex];
  var name = selopt.text;
  var index = selopt.value;
  return index;
}

//delete a node
function Delete()
{
  var querymap = CreateGraphParamMap();
  querymap[ cmdParam ] = "del " + GetNameIndexFromCombo( document.mapgenerator.deletecombo );
  RedrawWithParams( querymap );
}

//rename a node
function Rename()
{
  var querymap = CreateGraphParamMap();
  var newname = document.mapgenerator.newname.value;
  querymap[ cmdParam ] = "rename " + GetNameIndexFromCombo( document.mapgenerator.renamecombo ) + " " + newname;
  RedrawWithParams( querymap );
}

//move a child from one node to another in one operation
function SetParent()
{
  var querymap = CreateGraphParamMap();
  var parentcombo = document.mapgenerator.setparentcmb;
  var childcombo = document.mapgenerator.setchildcmb;
  querymap[ cmdParam ] = "setparent " + GetNameIndexFromCombo( childcombo ) + " " + GetNameIndexFromCombo( parentcombo );
  RedrawWithParams( querymap );
}

//connect 2 nodes
function Connect()
{
  var querymap = CreateGraphParamMap();
  var parentIndex = GetNameIndexFromCombo( document.mapgenerator.connectParent );
  var childIndex = GetNameIndexFromCombo( document.mapgenerator.connectChild );
  querymap[ cmdParam ] = "connect " + parentIndex + " " + childIndex;
  RedrawWithParams( querymap );
}

//disconnect 2 nodes
function Disconnect()
{
  var querymap = CreateGraphParamMap();
  var parentIndex = GetNameIndexFromCombo( document.mapgenerator.disconnectParent );
  var childIndex = GetNameIndexFromCombo( document.mapgenerator.disconnectChild );
  querymap[ cmdParam ] = "disconnect " + parentIndex + " " + childIndex;
  RedrawWithParams( querymap );
}

//schedule the redraw
function Redraw()
{
  var querymap = CreateGraphParamMap();
  RedrawWithParams( querymap )
}

function RedrawWithParams( params )
{
  setStatus( "Redrawing..." );
  var url = getBaseURL() + RedrawServer;

  //add stored graph data
  params[ "graph" ] = storedGraph;

  //make into strings
  var query = MochiKit.Base.queryString( params );
  //make a post request
  makePOSTRequest( url, query );
}

//process post request
function PostProcessMap()
{
  var http_request = RunningSearches[ 0 ]

  if( http_request.readyState == 4)
  {
    if (http_request.status == 200)
    {
      var meta = new Object();
      meta.responseText = http_request.responseText;
      ProcessMap( meta );
    }
    else
    {
      AsyncFailed( null );
    }
  }
}

function AsyncFailed( err )
{
  SetStatus( SearchFailed );
  RunningSearches[ 0 ] = null;
  alert( "Failed" );
}

//schedule the map
function DoMap()
{
  //set status
  setStatus( "Scanning..." );
  setHaveStoredGraph( null ); //no stored graph

  var url = getBaseURL() + GenMapServer;
  var query = MochiKit.Base.queryString( CreateGraphParamMap() );

  url = url + "?" + query;

  var d = MochiKit.Async.doSimpleXMLHttpRequest( url );

  d.addCallbacks(ProcessMap, AsyncFailed);

  RunningSearches[ 0 ] = d;
}

//extract a xml type tag content from a string
function parseTag( tag, text )
{
  var ret = ""
  var startTag = "<" + tag + ">";
  var endTag = "</" + tag + ">";

  var tagStart = text.indexOf( startTag  );
  var tagEnd = text.indexOf( endTag );

  if( tagStart != -1 && tagEnd != -1 )
  {
    ret = text.substring( tagStart + startTag.length, tagEnd );
  }

  return ret;
}

//return array of individual area strings
function ParseAreaElements( maptext )
{
  //seperate out area strings
  var startArea = "<area ";
  var endArea = ">";
  var AreaStrList = new Array();

  var start = maptext.indexOf( startArea );
  var end = maptext.indexOf( endArea );

  do
  {
    //get next area
    var areastr =  maptext.substring( start + startArea.length, end );

    AreaStrList.push( areastr );
    //slice up maptext
    maptext = maptext.substring( end + 1, maptext.length );

    start = maptext.indexOf( startArea );
    end = maptext.indexOf( endArea );
  }
  while( maptext.length > 0 && start != -1 && end != -1 )

  return AreaStrList;
}
//return list of area objects
function ParseAreaText( maptext )
{
  var AreaStrList = ParseAreaElements( maptext );

  var AreaDOMList = new Array();
  //iterate over these
  for( var i in AreaStrList )
  {
    var areastr = AreaStrList[ i ];

    //chomp this, sep out \w+\=\w+ matches
    var nextspace = areastr.indexOf( " " );

    //defaults
    var shapeval = "rect";
    var hrefval = "";
    var titleval = "";
    var coordsval = "";

    while( areastr.length > 0 )
    {
      var elemequal = ""
      if( nextspace != -1 )
      {
        elemequal = areastr.substring( 0, nextspace );
      }
      else
      {
        elemequal = areastr;
        areastr = "";
      }

      var length = areastr.length;

      areastr = areastr.substring( nextspace + 1, length );

      //split around =
      var equalIndex = elemequal.indexOf( "=" );

      var property = elemequal.substring( 0, equalIndex );

      var value = elemequal.substring( equalIndex + 1, elemequal.length );

      if( property == "shape" )
      {
        //remove "'s
        shapeval = value.substring( 1, value.length - 1 );
      }
      else if( property == "href" )
      {
        //remove "'s
        hrefval = value.substring( 1, value.length - 1 );
      }
      else if( property == "title" )
      {
        //remove "'s
        titleval = value.substring( 1, value.length - 1 );
      }
      else if (property == "coords" )
      {
        coordsval = value.substring( 1, value.length - 1 );
      }

      //chomp this, sep out \w+\=\w+ matches
      nextspace = areastr.indexOf( " " );
    }

    var areaDOM = MochiKit.DOM.createDOM( "area", {'shape': shapeval, 'href' : hrefval,
      'alt' : titleval, 'title' : titleval, 'coords' : coordsval} );
    AreaDOMList.push( areaDOM );
  }

  return AreaDOMList;
}

function DisplayMapText( maptext )
{
  //display map text to user
  var maptextNode = document.getElementById( "maptext" );

  //create text area
  var TextAreaNode = TEXTAREA({'rows': 10, 'cols' : 50 });

  //display text
  var dispmap = '<map name="layout" id ="layout" >' + maptext + '</map>'
  MochiKit.DOM.appendChildNodes( TextAreaNode, dispmap );

  //swap out mapNodeParent children
  MochiKit.DOM.replaceChildNodes( maptextNode, TextAreaNode );

}

//insert the map text into DOM, so usable and display for the user to copy out
function InsertMapText( maptext )
{
  DisplayMapText( maptext );

  var mapNodeParent = document.getElementById( "mapnode" );

  //create map node
  var mapnode = MochiKit.DOM.createDOM( "map", {'name': mapID, 'id' : mapID} );

  //now need to parse area
  var areaNodeList = ParseAreaText( maptext );

  //append children
  for( var i in areaNodeList )
  {
    var areaDOM = areaNodeList[ i ];
    MochiKit.DOM.appendChildNodes( mapnode, areaDOM );
  }

  //swap out mapNodeParent children
  MochiKit.DOM.replaceChildNodes( mapNodeParent, mapnode );
}

//parse a python stringlist into a javascript array
function parsePythonStrList( strList )
{
  //if blank shortcut
  if( strList.length == 0 || strList == "[]" )
  {
    return new Array();
  }

  //list encoded as ['string1','string2',....]
  //remove start and end braces
  var parseList = strList.substring( 1, strList.length -1 );

  //split on ,
  var scratchArray = new Array();
  scratchArray = parseList.split( ', ' );

  //need to go through each elements and remove quotes
  for(x in scratchArray)
  {
    scratchArray[ x ] = scratchArray[ x ].substring( 1, scratchArray[ x ].length - 1 );
  }

  return scratchArray;
}

//fill combo with vertex names
function FillNameCombo( combo )
{
  clearCombo( combo );

  for (var k in VertexNameArray)
  {
    var name = VertexNameArray[k];
    var opt = OPTION( null );

    //clip name if too long
    if( name.length > maxTitleDisp )
    {
      name = name.substring( 0, maxTitleDisp );
    }

    opt.text = name;
    opt.value = k;

    combo.options.add( opt );
  }
}

//scan and graphing completed
function ProcessMap( meta )
{
  RunningSearches[ 0 ] = null;
  var responseText = meta.responseText;

  var state = parseTag( "State", responseText );

  if( state == "OK" )
  {
    setStatus( "Loading data..." );

    //rem graph
    setHaveStoredGraph( parseTag( "graph", responseText ) );

    //get map text
    var maptext = parseTag( "Map", responseText );

    InsertMapText( maptext );

    //remember vertex names in order as IE can't do, well anything
    var nodenameList = parseTag( "NodeNames", responseText );
    VertexNameArray = parsePythonStrList( nodenameList );

    FillNameCombo( document.mapgenerator.deletecombo );
    FillNameCombo( document.mapgenerator.renamecombo );
    FillNameCombo( document.mapgenerator.connectParent );
    FillNameCombo( document.mapgenerator.connectChild );
    FillNameCombo( document.mapgenerator.disconnectParent );
    FillNameCombo( document.mapgenerator.disconnectChild );
    FillNameCombo( document.mapgenerator.setparentcmb );
    FillNameCombo( document.mapgenerator.setchildcmb );

    //sort out image
    var imgfile = parseTag( "GifFile", responseText );

    //last part allows save as
    var imgsrc = getBaseURL() + imageServer + "?" + MochiKit.Base.queryString( ["image"], [ imgfile ] );

    //insert image
    var imageplaceNode = document.getElementById( imageID );

    //create image node
    var anchorMap = "#" + mapID;
    var imageNode = IMG({'src': imgsrc, 'alt' : imgsrc, 'usemap' : anchorMap })
    MochiKit.DOM.replaceChildNodes( imageplaceNode, imageNode );
    //define image area
    imageplaceNode.style.width = "800px";
    imageplaceNode.style.height = "400px";
    imageplaceNode.style.overflow = "auto";
    imageplaceNode.style.borderWidth = "thin";
    imageplaceNode.style.borderColor = "red";

    //display map text
    VisibleDiv( "after_run", 1 );

    //enable node editing
    displayEditOption( true );
    setStatus( "Completed" );
  }
  else
  {
    displayEditOption( false );
    var msg = "Server error. The webmaster has been notified\n. Apologies, the application will be fixed shortly.";
    setStatus( msg );
    alert( msg );
  }


}
