var get_holidays_path = "/cgi/wl/fxmarkethours/get_market_holidays.pl";

var market = new Array (
        //do NOT use string colors (e.g 'yellow'). Problematic with IE.
	new FXMarket(0,"Sydney"   , '#009900', 22*60+0,  7*60+0, -10*60, 1, 'AUD', 'SYD'),
	new FXMarket(1,"Tokyo"    , '#FFBE0F',  0*60+0,  9*60+0,  -9*60, 0, 'JPY', 'TOK'),
	new FXMarket(2,"London"   , '#996600',  8*60+0, 17*60+0,      0, 1, 'GBP', 'LON'),
	new FXMarket(3,"New York" , '#6699CC', 13*60+0, 22*60+0,   5*60, 2, 'USD', 'NYC') 
	);
var tZone = new Array (
	new TimeZone(10*60, "Hawaii-Aleutian Standard Time", "(GMT-10) HST"),
	new TimeZone( 9*60, "Alaska Standard Time, Hawaii-Aleutian Daylight Time", "(GMT-9) AKST,HADT"),
	new TimeZone( 8*60, "Pacific Standard Time, Alaska Daylight Time", "(GMT-8) PST,AKDT"),
	new TimeZone( 7*60, "Mountain Standard Time, Pacific Daylight Time", "(GMT-7) MST,PDT"),
	new TimeZone( 6*60, "Central Standard Time, Mountain Daylight Time", "(GMT-6) CST,MDT"),
	new TimeZone( 5*60, "Eastern Standard Time, Central Daylight Time", "(GMT-5) EST,CDT"),
	new TimeZone( 4*60, "Atlantic Standard Time, Eastern Daylight Time", "(GMT-4) AST,EDT"),
	new TimeZone( 3*60+30, "Newfoundland Standard Time", "(GMT-3:30) NST"),
	new TimeZone( 3*60, "Atlantic Daylight Time", "(GMT-3) ADT"),
	new TimeZone( 2*60+30, "Newfoundland Daylight Time", "(GMT-2:30) NDT"),
	new TimeZone( 2*60, "", "(GMT-2)"),
	new TimeZone( 1*60, "", "(GMT-1)"),
	new TimeZone( 0, "Greenwich Mean Time, Western European Time", "GMT,WET"),
	new TimeZone(-1*60, "British Summer Time, Central European Time, Western European Daylight Time", "(GMT+1) BST,CET,WEDT"),
	new TimeZone(-2*60, "Eastern European/South African Standard Time, Central European Daylight Time", "(GMT+2) EET,SAST,CEDT"),
	new TimeZone(-3*60, "Moscow Time, Eastern European Daylight Time", "(GMT+3) MSK,EEDT"),
	new TimeZone(-4*60, "", "(GMT+4)"),
	new TimeZone(-5*60, "", "(GMT+5)"),
	new TimeZone(-(5*60+30), "Indian Standard Time", "(GMT+5:30) IST"),
	new TimeZone(-6*60, "", "(GMT+6)"),
	new TimeZone(-7*60, "Christmas Island Time", "(GMT+7) CXT"),
	new TimeZone(-8*60, "Singapore/Hong Kong/Chinese/Australian Western Standard Time", "(GMT+8) SGT,HKT,AWST"),
	new TimeZone(-9*60, "Japan/Korea Standard Time", "(GMT+9) JST,KST"),
	new TimeZone(-(9*60+30), "Australian Central Standard Time", "(GMT+9:30) ACST"),
	new TimeZone(-10*60, "Australian Eastern Standard Time", "(GMT+10) AEST"),
	new TimeZone(-(10*60+30), "Australian Central Daylight Time", "(GMT+10:30) ACDT"),
	new TimeZone(-11*60,"Australian Eastern Daylight Time", "(GMT+11) AEDT"),
	new TimeZone(-12*60, "New Zealand Standard Time", "(GMT+12) NZST")
	);

var closed_market_color = '#9F9F9F'; //greyish
var timeZoneOffset = (new Date()).getTimezoneOffset(); //default timeZone
var cellWidth, cellHeight, tableWidth, statTableWidth;
var showClocks = 0;

var fxmh_style = "font-size:10pt;";

var compressedMode = gup('compressedMode');
if (compressedMode == "true" || compressedMode == "1") {
  //alert(compressedMode);
  compressedMode = 1;
  cellWidth  = 8;
  cellHeight = 20;
  tableWidth = 24*cellWidth;
  statTableWidth = 195;
  fxmh_style = "font-size:8pt;";

} else { 
  compressedMode = 0;
  cellWidth  = 20;
  cellHeight = 20;
  tableWidth = 24*cellWidth;
  //statTableWidth = 380;
}

var the_timeout;

//****************************************************//
//get URL parameters (src: Netlobo.com)
function gup(name)
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}
//****************************************************//
function TimeZone(offset,title,text) {
  this.offset = offset;
  this.title = title;
  this.text = text;
}
//****************************************************//
function FXMarket(id,marketName,color,openTime,closeTime,timeZoneOffset,observeDST,currencyName,marketShortName) {
  this.id = id; //should be unique for each market
  this.name = marketName;
  this.shortName = marketShortName;
  this.color = color;
  this.GMTopenTime = openTime;//in minutes (from 12am)
  this.GMTcloseTime = closeTime;
  this.openTime = 0; //open time in the selected time zone
  this.closeTime = 0;
  this.timeZoneOffset = timeZoneOffset;
  this.observeDST = observeDST;
  this.layerA_id= 'm'+id+'a'; //string id for market's graphical layers
  this.layerB_id= 'm'+id+'b';
  this.layerA_closed = 0; // close or open status of market at layerA
  this.layerB_closed = 0; // close or open status of market at layerB

  this.isClosed = isMarketClosed; //function

  //local holidays (except weekends) during which market is closed.
  //data is acquired through an AJAX call to get_market_holidays.pl  
  this.holidays = ''; //e.g.'01.01.2006 ~New Year~ 25.12.2006 ~Christmas~ ';

  var mk = this;
  this.initHolidays = function initMarketHolidays() {
    //process AJAX response and init market holidays
    var myajax=mk.ajaxpack.ajaxobj;
    var myfiletype=mk.ajaxpack.filetype;
    if (myajax.readyState == 4){ //request of file completed
      if (myajax.status==200 || window.location.href.indexOf("http")==-1){ 
        //request was successful or running script locally
        if (myfiletype=="xml") {
	  var xmlDoc = myajax.responseXML;
  	  mk.holidays += xmlDoc.getElementsByTagName(currencyName).item(0).firstChild.data;
	  //alert(mk.holidays); 
	  refreshStat();
  	}
        else { //should never happen
  	  alert(mk.name+": Holiday data has the wrong format."); //myajax.responseText
        } 
      }
      else {
        //AJAX request failed
        //alert("Could not acquire list of bank holidays for "+mk.name+ " market.");
      }
    }
  } //end function initMarketHolidays

  this.ajaxpack = createAjaxPack();

  var d = new Date();
  var day  = d.getDate();
  var month= d.getMonth()+1;
  var year = d.getFullYear();
  var fromDate = day + "." + month + "." + year;

  var postRequest = "currencyName="+currencyName+"&fromDate="+fromDate;
  try {
     this.ajaxpack.postAjaxRequest("http:"+this.ajaxpack.basedomain+get_holidays_path, 
                                   postRequest, this.initHolidays, "xml");
  } catch(e) {
     //alert("Could not acquire list of bank holidays for "+this.name+ " market.");
  }
}

//****************************************************//
function isMarketClosed(date) {
  //if market is closed, returns the holiday description, else returns nothing

  //assuming all markets are closed on Saturdays and Sundays (FIX?)
  if (date.getDay() == 0) {return 'Weekend(Sunday)';}
  if (date.getDay() == 6) {return 'Weekend(Saturday)';}

  var holidays = this.holidays;
  var dateStr = dateToStr(date);
  var ix = holidays.indexOf(dateStr);
  if (ix == -1) {
    return ''; //there is no local holiday for the specified market (it is not closed)
  } else {
    ix = holidays.indexOf("~",ix); //holiday's description begins with ~
    if (ix == -1) {
      //alert('Holiday description not found!');
      return 'unknown';
    }
    var end_ix = holidays.indexOf("~",ix+1); //the description ends with ~
    if (end_ix == -1) {
      //alert('Wrong format for holiday description!');
      return 'unknown';
    }
    //alert ( this.name + " is closed on " + date + " for: " + holidays.substring(ix+1,end_ix) );
    return holidays.substring(ix+1,end_ix);
  }
}

//****************************************************//
function computeDST_diff(observeDST) {
  //difference between current time and std time. If it is Daylight Saving Time (DST),
  //and the market observes it, the difference is 60 minutes otherwise 0.

  if (!observeDST) { return 0; } //when market dosen't observe DST

  var dst_start = new Date(); 
  var dst_end = new Date();

  if (observeDST == 2) {

    //compute 2nd Sunday of March (for NYC in 2007)
    dst_start.setMonth(2); // Set from month 0-11
    dst_start.setDate(1);
    var wday = dst_start.getDay();// what weekday is 31st?
    if ( wday == 0 /* as in sunday */) { wday = 7; }
    dst_start.setDate(15-wday); // subtract from the highest possible value of the second sunday (+1 because of array issues)

    //compute first Sunday of November
    dst_end.setMonth(10); // pick November
    dst_end.setDate(1);
    wday = dst_end.getDay();
    if ( wday == 0 /* as in sunday */) { wday = 7; }
    dst_end.setDate(8 - wday);

  } else {
    //compute last Sunday of March
    dst_start.setMonth(2);
    dst_start.setDate(31);
    var wday = dst_start.getDay();// what weekday is 31st?
    dst_start.setDate(31-wday); // go back until last Sunday

    //compute last Sunday of October
    dst_end.setMonth(9);
    dst_end.setDate(31);
    wday = dst_end.getDay();
    dst_end.setDate(31-wday);
  }

  var current = new Date();
  if (current >= dst_start && current < dst_end) { return 60; } else { return 0; }
}

//****************************************************//
function adjustMarketHours() {
  //adjust market hours by converting GMT times to the timeZone time
  
  for (var row=0; row<market.length;row++) {
    //current difference with standard time (in minutes)
    var DST_diff = computeDST_diff(market[row].observeDST);
    market[row].openTime = (market[row].GMTopenTime - timeZoneOffset - DST_diff + 24*60)%(24*60);
    //alert("opentime for "+ market[row].name + " is " + market[row].openTime);
    market[row].closeTime = (market[row].GMTcloseTime - timeZoneOffset - DST_diff + 24*60)%(24*60);
  }
}

//****************************************************//
function adjustMarketLayers() {
  var num_rows = market.length;
  for (var row = 0 ; row < num_rows; row++) {
    var openPos = tableWidth * market[row].openTime / (24*60),
        closePos= tableWidth * market[row].closeTime / (24*60),
        top = (row+1+.1)*cellHeight, //to add some space between each layer (row)
        height = .9*cellHeight,      //same as above
        marketLayer_a = market[row].layerA_id,
        marketLayer_b = market[row].layerB_id,
	marketName = (compressedMode) ? market[row].shortName : market[row].name;

    //for drawing purposes, closetime of 00:00 is equal to 24:00 so adjust closePos
    if (closePos == 0) {closePos = tableWidth;}

    //open times are always represented in layer_a
    if (closePos > openPos) {
      showDiv(marketLayer_a);
      hideDiv(marketLayer_b);
      modifyMarketLayer(marketLayer_a,openPos, top, closePos-openPos, height, marketName);
    } else {
      showDiv(marketLayer_a);
      modifyMarketLayer(marketLayer_a, openPos, top, tableWidth-openPos, height, marketName);
    
      showDiv(marketLayer_b);
      modifyMarketLayer(marketLayer_b, 0, top, closePos, height, marketName);
    }

    //change color of closed layers to grey
    if ( market[row].layerA_closed == 1 ) 
      chColorDiv(marketLayer_a, closed_market_color);
    else 
      chColorDiv(marketLayer_a, market[row].color);

    if ( market[row].layerB_closed == 1 )
      chColorDiv(marketLayer_b, closed_market_color);
    else
      chColorDiv(marketLayer_b, market[row].color);

  }
}

//****************************************************//
function processHolidays (current_date) {
  // (current_date is the local time for the seleted time-zone)
  // shadow market layers a or b which are closed because of a holiday or weekend
  // (based on the market's local open date)

  var time_tmp;
  var num_rows = market.length;
  for (var row = 0 ; row < num_rows; row++) {
    var opday = new Date();
    opday.setTime(opday.getTime());
    opday.setHours(0);
    opday.setMinutes(0);
    opday.setSeconds(0);

    if ( current_date > opday && current_date.getDay() != opday.getDay() ) {
      //alert("open date is the next day. readjust by +24 hours");
      opday.setTime( opday.getTime() + 24*60*60*1000 );
    }

    //compute local openning date of market (in minutes from 12am)
    time_tmp = market[row].openTime - market[row].timeZoneOffset - (-timeZoneOffset);// extra '-' to convert from string to integer

    opday.setTime( opday.getTime() + time_tmp*60*1000 );
    //used setTime instead of setMinutes because Safari's setMinutes only work with 0 <= x <= 59

    //alert(market[row].name + ' opens on ' + opday);

    if ( market[row].isClosed(opday) ) {
      //alert('A:'+market[row].name + ' CLOSED on ' + opday+ 'cur='+current_date);
      market[row].layerA_closed = 1;
    } 
    else {
      //alert('A:'+market[row].name + ' NOT CLOSED on ' + opday+ 'cur='+current_date);
      market[row].layerA_closed = 0;
    }

    //find the next day on which the market opens
    //for example on Friday of a long weekend, market will open on Tuesday
    var next_opday = new Date(opday);
    do {
      next_opday.setTime(next_opday.getTime() + 24*3600*1000); //in miliseconds
    } while (market[row].isClosed(next_opday));

    //convert next open date back to the selected time-zone
    time_tmp = market[row].timeZoneOffset - timeZoneOffset;
    next_opday.setTime(next_opday.getTime() + time_tmp*60*1000);
    market[row].nextOpenDay = next_opday;
        
    //check market closure on the previous day
    opday.setTime(opday.getTime() - 24*3600*1000);
    if ( market[row].isClosed(opday) ) {
      //alert('B:'+market[row].name + ' CLOSED on ' + opday+ 'cur='+current_date);
      market[row].layerB_closed = 1;
    } 
    else {
      //alert('B:'+market[row].name + ' NOT CLOSED on ' + opday+ 'cur='+current_date);
      market[row].layerB_closed = 0;
    }

  }
}
//****************************************************//
var z_index = 100;
function mkLayer(id,left,top,width,height,color,text)
{ //generates the HTML code for a market layer
  var code = '<div class="mLayer" id="'+id+'" style="left:' + left + 'px;top:' + top + 'px;';
  code += 'height:' + height + 'px;width:' + width + 'px;';
  code += 'background-color:' + color + ';z-index:' + z_index +';" >';
  code +=  text + '</div>';
  z_index++;
  return code;
}

//****************************************************//
function modifyMarketLayer(divID,left,top,width,height,name) {
  //IE wont resize a layer smaller than its text contents area, if this
  //happens, we should empty the layer contents before resizing and later
  //rewrite its contents back

  moveDiv(divID,left,top);

  if (width <= 4*cellWidth) {rwDiv(divID,"\255");} //div conetents should not be empty
  else {
    var content = '<div style="' + fxmh_style + '">'+name+'</div>';  
    rwDiv(divID,content);
  }
  
  resizeDiv(divID,width,height);
}

//****************************************************//
function genStatus(current_date) {
  //generate html code for the status section (current time + markets' status)

  //current_date is the local time for the seleted time-zone
  var htmlCode='<div style="' + fxmh_style + '">';
  htmlCode += 'FX markets on ';
  if (compressedMode) htmlCode += '<br>';
  htmlCode += datetimeToStr(current_date) + '<br>';
  //FIX: below table attributes should be moved to css file when they are supported
  htmlCode += '<table border="2" cellspacing="1" cellpadding="2" width="'+statTableWidth+'">';

  var current = current_date.getHours()*60 + current_date.getMinutes();
  var num_rows = market.length;

  for (var row=0; row < num_rows; row++) {
    var opn = market[row].openTime;
    var cls = market[row].closeTime;
    var status = 'closed';

    htmlCode += '<tr>';
    htmlCode += '<td style="'+fxmh_style+'">'+ market[row].name +'</td>';
//'<td class="fxmh">'+ market[row].name +'</td>';

    var time_remained = -1;
    if ( (current < cls) &&
         ( (current >= opn && !market[row].layerA_closed) || (opn > cls && !market[row].layerB_closed) ) ) {
      status = 'open';
      time_remained = cls - current;
    }
    else if (current >= opn && opn > cls && !market[row].layerA_closed) {
      status = 'open';
      time_remained = 24*60 + cls - current;
    }
    else {
      status = 'closed';
      time_remained = (24*60 + opn - current)%(24*60);
    }

    var minutes = time_remained % 60;
    var hours = (time_remained - minutes)/60;

    if (!compressedMode) {
      hours = (hours=='1') ? '1 hour ' : hours + ' hours ';
      minutes = (minutes=='1') ? '1 minute ' : minutes + ' minutes ';
    } else {
      hours = '<br>' + hours + 'h ';
      minutes = minutes + 'm ';
    }
            
    if (status=='open') {
      htmlCode += '<td style="'+fxmh_style+'" class="statOpen">open</td>';
      htmlCode += '<td style="'+fxmh_style+'" class="statTxt">closes in '+hours+minutes+'</td>';
    }
    else if (status=='closed') {
      htmlCode += '<td style="'+fxmh_style+'" class="statClose">closed</td>'; 
      if (current < opn && !market[row].layerA_closed) {
        htmlCode += '<td style="'+fxmh_style+'" class="statTxt">opens in '+hours+minutes+'</td>';
      } else {
        htmlCode += '<td style="'+fxmh_style+'" class="statTxt">opens on '+datetimeToStr(market[row].nextOpenDay)+'</td>';
      }
    }    
    htmlCode += '</tr>';
  }
  htmlCode += '</table></div>';
  return htmlCode;
}

//****************************************************//
function setTimer() {
  //refresh stats every minute
  if ((new Date()).getSeconds() == 0) { 
    refreshStat();
  }
  
  //set timer for 1 sec
  the_timeout = setTimeout("setTimer();", 1*1000); 
}
//****************************************************//
function refreshStat() {
  var current_date = new Date();
  //convert time for the selected timeZone
  var time_tmp = current_date.getTimezoneOffset() - timeZoneOffset;
  current_date.setTime(current_date.getTime() + time_tmp*60*1000);

  //tags markets closed because of a holiday or weekend 
  processHolidays(current_date);

  if (showClocks == 1) {
    //createFXMarketClocks();

  } else {
    adjustMarketLayers(current_date);
    //compute and adjust new location of the timeline
    var tm = current_date.getHours()*60 + current_date.getMinutes();//time in minutes
    moveDiv('line',tableWidth * tm / (24*60) - 3,0);
  
    //update markets status section
    rwDiv('statusDiv',genStatus(current_date));
  }

}
//****************************************************//
function initMarket() {
  adjustMarketHours();
  refreshStat(); 
}

//****************************************************//
function datetimeToStr(mydate) { 
  // output example: "Tue Feb 21, 2006 10:08 PM"
  var month = new Array ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
  var weekday = new Array ('Sun','Mon','Tue','Wed','Thu','Fri','Sat')
  var w = mydate.getDay();
  var d = mydate.getDate();
  var m = mydate.getMonth();
  var y = mydate.getFullYear();
  var dateStr = weekday[w] +' '+ month[m] +' '+ d + ', ' + y;

  // create time string (e.g. '11:02 AM'), alternative: current_date.toLocaleTimeString();
  var hours = mydate.getHours();
  if (hours>=12) {hours-=12;PM_AM = 'PM';}
  else PM_AM = 'AM';
  var minutes = mydate.getMinutes();  
  if (minutes < 10) {minutes = '0'+minutes;}  
  var timeStr = hours + ':' + minutes + ' ' + PM_AM;
  
  return dateStr+' '+timeStr;
}
//****************************************************//
function dateToStr(mydate) { 
  // output example: "02.03.2006", used as input to market[x].isClosed
  var d = mydate.getDate();
  if (d < 10) { d = '0' + d;} //make a 2-digit day
  var m = mydate.getMonth()+1;
  if (m < 10) { m = '0' + m;} //make a 2-digit month
  
  var str = d +"."+ m +"."+ mydate.getFullYear();
  return str;
}

//****************************************************//
function printMarketLayers() {
  
  var num_rows = market.length;
  for (var row = 0 ; row < num_rows; row++) {
    document.write( mkLayer(market[row].layerA_id,0,cellHeight*(row+1),
                    100,cellHeight,market[row].color,market[row].name) );
    document.write( mkLayer(market[row].layerB_id,200,cellHeight*(row+1),
                    100,cellHeight,market[row].color,market[row].name) );
  }

  //FXMarkets' table
  document.write('<table id="FXMarkets_table" cellpadding="0" cellspacing="0" frame="hsides" rules="groups" ');
  document.write('width="'+tableWidth+'">');
  //header:
  document.write('<thead><tr>');
  
  var step = 1;
  var clWidth = cellWidth;
  if (compressedMode) { step = 2; clWidth = 2*cellWidth;}
  
  for (var col=0,i=0; col < 24; i++,col+=step) {
    var color = (i%2==1) ? '#999999' : '#FFFFFF';
    document.write('<td style="'+fxmh_style+'" height="'+cellHeight+'" width="'+clWidth+'" align="center" colspan="'+step+'"');
    document.write('bgcolor="'+color+'">' + col + '</td>');	  
  }
  document.write("</tr></thead>");
  //body: empty rows, market layers will appear here
  var num_rows = market.length;
  for (var row = 0 ; row < num_rows; row++) {
    document.write("<tr>");
    for (var col=0; col < 24; col++) {
      document.write('<td style="'+fxmh_style+'" height="'+cellHeight+'" width="'+cellWidth+'" align="left"> </td>');
    }
    document.write('</tr>');
  }
  document.write('</table>');
}
//****************************************************//
function printTimeZoneSelector() {
  var tmpStr = '<div style="' + fxmh_style + '">';
  
  if (compressedMode) { tmpStr += 'TZ: <select name="time_zone" style="font-size:7pt"'; }
  else { tmpStr += 'Time Zone: <select name="time_zone" '; }

  tmpStr += ' onChange="timeZoneOffset=this.options[selectedIndex].value;initMarket();" >';
  document.write(tmpStr);
   
  for (var i=0; i<tZone.length;i++) {
    document.write('<option value="'+tZone[i].offset+'" ');
    if (tZone[i].title != "") { document.write('title="'+tZone[i].title+'"'); }
    if (tZone[i].offset == timeZoneOffset) { document.write(' selected '); }
    document.write('>'+tZone[i].text);
  }
  document.write('</select></div>');
}
//****************************************************//
