// Copyright Teemu Lätti 2011

// --- Coding helpers ---

function trace(msg)
{
  if (window.console)
    window.console.log(msg);
}

function assert(test)
{
  if (!test)
    alert("ASSERT failure!");
}

// --- Gfx ---

function RGB(r,g,b)      { return ((r&0xff)<<16) | ((g&0xff)<<8) | (b&0xff); }
function GetRValue(col)  { return (col & 0xff0000) >> 16; }
function GetGValue(col)  { return (col & 0x00ff00) >> 8;  }
function GetBValue(col)  { return (col & 0x0000ff);       }

//
// HttpRequest(url,callback,param)
//
// XMLHttpRequest wrapper. The specified callback method gets called with the page contents.
//
// Example:
//
//    { ...
//      HttpRequest("PageToRetrieve.php",MyCallback);
//      HttpRequest("PageToRetrieve.php",MyCallback,myparam);  // also passes parameter to the function
//    ... }
//
//    function MyCallback(pagetext)       { ... }
//    function MyCallback(pagetext,param) { ... }  // optional parameter
//
function HttpRequest(url,func,param)
{
  // Create HTTP request object
  var httpreq=null;
  if (window.XMLHttpRequest)
  {
    // code for IE7, Firefox, Opera, etc.
    httpreq=new XMLHttpRequest();
  }
  else if (window.ActiveXObject)
  {
    // code for IE6, IE5
    httpreq=new ActiveXObject("Microsoft.XMLHTTP");
  }

  // Call to get the page
  if (httpreq!=null)
  {
    // Open http request
    httpreq.open("GET",url,true); // open() must be called before setting any properties

    // Callback method
    httpreq.onreadystatechange=function()
    {
      if (httpreq.readyState==4)
      {
        // 4 = "loaded"
        var response;
        if (httpreq.status==200)
        {
          // 200 = "OK"
          response=httpreq.responseText;
        }
        else
        {
          response="(error)";
        }

        // Call specified callback function
        func(response,param);
      }
    };

    // Make http request
    httpreq.setRequestHeader("If-Modified-Since",new Date(0)); // bypass cache
    httpreq.send(null);
    return httpreq;
  }
  else
  {
    alert("Your browser does not support XMLHTTP.");
    return null;
  }
}

function CancelHttpRequest(httpreq)
{
  if (httpreq)
    httpreq.onreadystatechange=null;
}

//
// HTTP request with refresh as the response action
//
function HttpRequestRefresh(url)
{
  HttpRequest(url,function() { window.location.reload(); });
}

//
// Reload (==Refresh) current page
//
function ReloadPage()
{
  window.location.reload();
}
function RefreshPage()
{
  window.location.reload();
}

function OpenPage(page)
{
  window.top.location.assign(page);
}

function GetEventKeyCode(e)
{
  if (e && window.event) // IE
  {
    return e.keyCode;
  }
  else if (e && e.which) // Netscape/Firefox/Opera
  {
    return e.which;
  }
  else
  {
    return 0;
  }
}

//
// Element helpers
//

function Element(id)
{
  return document.getElementById(id);
}
function EnableElement(id,enabled)
{
  Element(id).disabled=!enabled;
}
function ShowElement(id,show)
{
  Element(id).style.display=(show) ? "inline" : "none";
}
function SetElementText(id,text)
{
  Element(id).innerHTML=text;
}
function SetElementHTML(id,text)
{
  if (Element(id)==null)
    trace("SetElementHTML: Element('" + id + "') is null");
  Element(id).innerHTML=text;
}
function SetElementValue(id,text)
{
  Element(id).value=text;
}
function GetElementValue(id)
{
  return Element(id).value;
}
function GetElementText(id)
{
  return Element(id).innerHTML;
}
function GetElementHTML(id)
{
  return Element(id).innerHTML;
}

//
// Timers
//

function SetTimer(timeout,func,param)
{
  // Timer with optional user parameter: SetTimeout(1000,MyFunction,[null]);

  // Setting parameter to null after using it only to force garbage collector
  // (because somebody wrote in the net that it might cause problems,
  //  I am not sure if it would be here because this is in own function)
  return setTimeout(function(){func(param); param = null},timeout);
}

function KillTimer(timerId)
{
  clearTimeout(timerId);
}

//
// Cupla variables
//

// Get value of variable async: value is returned as first parameter to the callback function
// (the other parameters "param" is optional)
function GetCuplaVar(name,func,param)
{
  var url="http://www.cupla.net/vars/get.php?name=" + name;
  HttpRequest(url,func,param);
}

// Get value of variable directly to element
function GetCuplaVarElement(name,id)
{
  GetCuplaVar(name,OnGetCuplaVarElement,id);
}
function OnGetCuplaVarElement(value,id)
{
  Element(id).innerHTML=value;
}

// Set value of variable async: function is called when setting is done (optional)
function SetCuplaVar(name,value,func,param)
{
  var url="http://www.cupla.net/vars/set.php?name=" + name + "&value=" + value;
  HttpRequest(url,func,param);
}

//
// Cupla coms
//

// Send data
//
// func(result,[param]) = gets called when data has been sent (recorded to SQL), result is only "OK"
//
function SendComs(from,to,data,func,param)
{
  var url='http://www.cupla.net/coms/send.php?from=' + from + '&to=' + to + '&data=' + data;
  HttpRequest(url,func,param);
}

// Receive data
//
// timeout                   = timeout in seconds
// func(sender,data,[param]) = gets called when a) data is received, or b) timeout expires (in this case sender and data are empty)
//
function ReceiveComs(to,timeout,bHTMLFormat,func,param)
{
  var ReceiveComsParam=new Object();
  ReceiveComsParam.func=func;
  ReceiveComsParam.param=param;
  var url='http://www.cupla.net/coms/receive.php?to=' + to + '&timeout=' + timeout + '&format=' + ((bHTMLFormat) ? 'html' : 'raw');
  HttpRequest(url,OnReceiveComs,ReceiveComsParam);
}
function OnReceiveComs(result,param)
{
  var i=result.indexOf("\n");
  if (i<0)
  {
    // Only one line => probably timeout as empty
    param.func("",result,param.param);
  }
  else
  {
    // First line=sender, second line onwards=data
    param.func(result.substr(0,i),result.substr(i+1),param.param);
  }
}

var g_nAnimNext  =0;  // Next free animation index
var g_nAnimActive=0;  // Index of the first active animation (anything before this index has been cancelled)

function CancelAnimations()
{
  // Cancel all current animations
  //trace("CancelAnimations()");
  g_nAnimActive=g_nAnimNext;  // All animations upto current point are now cancelled
}

function SetAnimTimer(msTotalTime,msStep,func,param,funcDone)
{
  // Set animation timer that travels from pos=[0,100]% over total time of msTotalTime.
  // Each step takes msStep (value 0 for step gives default).
  // For each step, function func(pos,param) gets called. Finally funcDone() gets called.

  // Animation
  var animTimer=new Object();
  animTimer.index      =g_nAnimNext++;  // Animation index
  animTimer.msTotalTime=msTotalTime;
  animTimer.msStep     =(msStep!=0) ? msStep : 20;  // default is 20ms per step
  animTimer.pos        =0;
  animTimer.func       =func;
  animTimer.param      =param;
  animTimer.funcDone   =funcDone;

  AnimTimerFunc(animTimer); // first step immediately (pos==0)
}

function AnimTimerFunc(animTimer)
{
  // Animation timer
  assert(animTimer.pos<=100);
  if (animTimer.index<g_nAnimActive)
  {
    // Animation has been cancelled => quit
    //trace("AnimTimerFunc() cancel");
  }
  else
  {
    // Animation step

    // First set upcoming step so timing will be exact
    var pos=animTimer.pos;
    if (animTimer.pos<100)
    {
      animTimer.pos+=100/(animTimer.msTotalTime/animTimer.msStep)+0.5;
      if (animTimer.pos>100)
        animTimer.pos=100;  // it is vital for animations that the last call is always with 100%
      SetTimer(animTimer.msStep,AnimTimerFunc,animTimer);
    }

    // Now call animation timer callback
    animTimer.func(pos,animTimer.param);

    // Call done callback if this is the last step
    if (pos==100)
    {
      if (animTimer.funcDone)
        animTimer.funcDone();
    }
  }
}

function AnimFadeElement(elem,time,beg,end,OnDoneFunc)
{
  // Fade element 'elem' (object) from opacity (0.0-1.0) 'beg' to 'end' in total time (ms) 'time' and then call function OnDoneFunc

  assert(elem);

  var fade=new Object();
  fade.element=elem;
  fade.beg=beg;
  fade.end=end;

  SetAnimTimer(time,0,AnimFadeElementTimer,fade,OnDoneFunc);
}

function AnimFadeElementTimer(pos,fade)
{
  var opa=mapvalue(pos,0,100,fade.beg,fade.end);
  SetElementOpacity(fade.element,opa);
}

function AnimFadeElementColor(elem,color1,color2,funcDone)
{
  assert(elem);

  var fade=new Object();
  fade.element=elem;
  fade.color1=color1;
  fade.color2=color2;

  SetAnimTimer(300,0,AnimFadeElementColorTimer,fade,funcDone);
}

function AnimFadeElementColorTimer(pos,fade)
{
  // Color fading //! to method
  var r1=GetRValue(fade.color1);
  var g1=GetGValue(fade.color1);
  var b1=GetBValue(fade.color1);
  var r2=GetRValue(fade.color2);
  var g2=GetGValue(fade.color2);
  var b2=GetBValue(fade.color2);
  var rd=mapvalue(pos,0,100,r1,r2);
  var gd=mapvalue(pos,0,100,g1,g2);
  var bd=mapvalue(pos,0,100,b1,b2);
  var col=RGB(rd,gd,bd);

  var str=col.toString(16); //! to helper method
  while (str.length<6)      //! to helper method
    str='0'+str;
  fade.element.style.color="#" +str;
}

function IsOpacitySupported()
{
  // In IE8 and older, you must use style.filter='alpha(opacity:100)' instead of proper style.opacity=1.0;
  return (document.body.style.opacity!=undefined);
}

function SetElementOpacity(elem,opacity)
{
  if (IsOpacitySupported())
    elem.style.opacity=opacity;
  else
  {
    // IE8 and older
    if (opacity==1)
      elem.style.filter=''; // for some reason, filter with opacity 100 corrupts the image, so clear it here
    else
      elem.style.filter='alpha(opacity=' + opacity*100 + ')';
  }
}

// FadeImg - Image that automatically fades in after loading
//
// NOTE: also use CSS class "FadeImg" to set opacity to zero by default
//
function FadeImg_onload(pthis)
{
  AnimFadeElement(pthis,200,0,1);
}

// Map value from range [min1,max1] to [min2,max2]
//
function mapvalue(value,min1,max1,min2,max2)
{
  return (min2 + (value-min1)/(max1-min1)*(max2-min2));
}

