/**
 * Controller
 *
 * Singleton pattern Controller object that will take care of user interaction
 *
 * @author     Takashi Mizohata <takashi@firstbornmultimedia.com>
 * @copyright  2008 firstborn
 * @license    firstborn internal (pending)
 * @TODO       STRONGLY RECOMMENDED REFACTORING!
 * @NOTE       for Member methods named: init*** cannot use 
 *             Controller.getInstance(), use "this" keyword instead.
 * @NOTE       for Member methods named: on*** cannot use "this" keyword, 
 *             use Controller.getInstance() instead.
 */


// SINGLETON PATTERN ______________________________________________________

var Controller = function()
{
  throw new Error("This is Singleton pattern. Please use getInstance() instead.");
}


Controller.getInstance = function ()
{
  Controller.initObject();
  return Controller.getInstance();
}

/*
 * @NOTE  by this implementation, you cannot call getInstance() in certain
 * code point such as init sequence... is it natural?  Maybe.
 */
Controller.initObject = function ()
{
  delete Controller.getInstance;

  var InnerClass = function ()
  {
    for (var i in Controller.prototype)
    {
      this[i] = Controller.prototype[i];
    }
    // initInstace is a conventional name for an instance initializer!!
    this.initInstance();
    delete this.initInstance;
  }
  var instance = new InnerClass();
  Controller.getInstance = function ()
  {
    return instance;
  }

  delete Controller.initObject;
}


// ACTUAL IMPLEMENTATION __________________________________________________


Controller.prototype.initInstance = function ()
{
  //console.log('initInstance');

//  try
//  {
    // initilize member variables
    this.dictionary = new Object.associativeArray();

    var jq_w = $('#pageNav .wrap');
    var jq_p = $('#pageNav .primary img');
    var jq_s = $('#pageNav .secondary img');

    // enchance hover effects
    this.enhanceSwap(jq_p);
    this.enhanceSwap(jq_s);

    // enhance search box
	if($('#pageLogo .search input').get(0) != "undefined")
		this.initSearchBox(); // prevent javascript errors on pages where there is no loupe

    // enhance comparison table hover
    var hover_bg_path = "/images/product/compare_bg-hover.gif";
    this.initComparisonTableHover(hover_bg_path);

    // enhance promo slots
    this.initPromoSlots();

    // enhance Location Selector
    this.initLocSelector();

    // enhance Sidebar Collapsable List
    //this.initSidebarUL();

    // paginate New Headlines on Sidebar
    this.initPaginateNewsHeader();

    // enhance Locate Retailer page
    this.initLocateRetailer();

    // enhance tab functionality
    this.initTabTables();

//  }
//  catch (ex)
//  {
//    console.log(ex);
//  }
}


Controller.prototype.initTabTables = function ()
{

  if (typeof(TabTable) == 'function')
  {
    new TabTable(this.dictionary);
  }

}


Controller.prototype.initLocateRetailer = function ()
{
  if (typeof(LocateRetailer) == 'function')
  {
    new LocateRetailer(this.dictionary);
  }
}


Controller.prototype.initPaginateNewsHeader = function ()
{
  if (typeof(PaginateNewsHeader) == 'function')
  {
    var dict = this.dictionary;
    var jq_data = $('#pageBody .side div.headlines');
    jq_data.each(
      function (n, obj)
      {
        try
        {
          var pnh = new PaginateNewsHeader(jq_data);
          var jq_act = $('.newsNav img', obj);
          jq_act.each (
            function (o, img)
            {
              dict.set(img, pnh);
            }
          );
          jq_act.bind('click', {'type': 'CLICK'}, PaginateNewsHeader.dispatch);
        }
        catch (err)
        {
          //console.log('this is important try~catch. Do not delete this.');
        }
      }
    );
  }
}


Controller.prototype.initSidebarUL = function ()
{
  var jq = $('#pageBody .side ul');
  var dict = this.dictionary;
  var arr = [];
  jq.each(
    function (n, obj)
    {
      try
      {
        var s = new SidebarUL(obj);
        dict.set(obj, s);
        arr[arr.length] = s.header;
      }
      catch(err)
      {
        //console.log('this is important try~catch. Do not delete this.');
      }
    }
  );

  $(arr).bind('click', {'type': 'CLICK'}, SidebarUL.dispatch);
}


Controller.prototype.initLocSelector = function ()
{
  var jq = $('#pageBody .map .selector');
  var len = jq.size();
  var groups = [];
  var dict = this.dictionary;
  for (var i = 0; i < len; ++i)
  {
	var ls = new LocSelector(jq.get(i));
	dict.set(jq.get(i), ls);
    groups[groups.length] = ls;
  }
  dict.set(document.body, groups);

  jq.bind('click', {'type': 'CLICK'}, LocSelector.dispatch);
}


Controller.prototype.initPromoSlots = function ()
{
  var jq_s = $('#pageBody .promos .promo .header');
  var jq_d = $('#pageBody .promos .promo .each');
  var jq_p = $('#pageBody .promos .promo');
  var dict = this.dictionary;
  var len = jq_s.size();
  if (len != jq_d.size())
  {
    throw new Error('length does not match in Promo, wrong structure.');
  }

  for (var i = 0; i < len; ++i)
  {
    var h = jq_s.get(i);
    var e = jq_d.get(i);
    var p = new Promo(h, e);
    dict.set(h, p);
    dict.set(e, p);
  }

  jq_s.bind('click', {'type': 'HEADER_CLICK'}, Promo.dispatch);
  jq_s.bind('mouseenter', {'type': 'HEADER_ENTER'}, Promo.dispatch);
  jq_p.bind('mouseleave', {'type': 'PROMO_LEAVE'}, Promo.dispatch);

//  jq_s.each(function (n, obj)
//  {
//    var promo = dict.get(obj);
//    promo.timerHandler = window.setTimeout($.scope(promo, promo.autoShow), 200);
//  });
  // newly added: June 26 2008
  // ________________________________________________________________________
  Controller.defer(
    $('#pageBody .promos .promo .each img'),
    function ()
    {
      jq_s.each(function (n, obj)
      {
        var promo = dict.get(obj);
        promo.timerHandler = window.setTimeout($.scope(promo, promo.autoShow), 200);
      });
    }
  );

}



Controller.prototype.initComparisonTableHover = function (path)
{
  // find objects
  var jq_tds = $('#pageBody table.compare tbody td');

  // enhance balls too.
  this.enhanceSwap($('#pageBody table.compare tbody img'), true);

  var dict = this.dictionary;
  var pare = null;
  jq_tds.each(
    function (n, obj)
    {
      var mypare = $(obj).parent().get(0);
      switch (true)
      {
        case (mypare == pare):
        break;

        case (pare == null):
        case (mypare != pare):
          pare = mypare

        default:
          dict.set(pare, new ComparisonHover(pare, path));
        break;
      }
    }
  );
  jq_tds.bind('mouseenter', this.comparisonOnHover);
  jq_tds.bind('mouseleave', this.comparisonOnBlur);
}

Controller.prototype.comparisonOnHover = function (ev)
{
  var c = Controller.getInstance();
  var t = $(ev.target).parents().filter('tr').get(0);
  var pathinfo = c.dictionary.get(t);
  pathinfo.hover(t);
}

Controller.prototype.comparisonOnBlur = function (ev)
{
  var c = Controller.getInstance();
  var t = $(ev.target).parents().filter('tr').get(0);
  var pathinfo = c.dictionary.get(t);
  pathinfo.out(t);
}



Controller.prototype.initSearchBox = function ()
{
  delete this.initSearchBox;
  var loupe = $('#pageLogo .search img');
  var wrap = $('#pageLogo .search .invisible');
  var input = $('#pageLogo .search input').get(0);
  var form = $('#pageLogo .search form').get(0);
  var loupe_elm = loupe.get(0);
  if(loupe_elm)
  {	this.dictionary.set(loupe_elm, new ImagePath(loupe_elm.src));
  }

  loupe.click(this.onClickLoupe);
  this.openSearchBox = function ()
  {
    wrap.css({'display':'inline'});
	input.focus();
	//input.value='temporarily unavailable';
	//input.css({'font-color':'red'});
  }
  this.closeSearchBox = function ()
  {
    wrap.css({'display':'none'});
  }
  this.clearQuery = function ()
  {
    input.value = '';
  }
  this.submitQuery = function ()
  {
    //alert('submitting query=> ' + input.name + ': ' + input.value);
	form.submit();
  }
  this.isSearchBoxOpen = function ()
  {
    var result = true;
    if (wrap.css('display') == 'none')
    {
      result = false;
    }
    return result;
  };
  this.isQueryEmpty = function ()
  {
    var result = false;
    if (input.value == '')
    {
      result = true;
    }
    return result;
  };
}

Controller.prototype.onClickLoupe = function (ev)
{
  var c = Controller.getInstance();
  var loupe_elm = ev.target;
  var loupe_path = c.dictionary.get(loupe_elm);
  if (!c.isSearchBoxOpen())
  {
    loupe_path.selected(loupe_elm);
    c.openSearchBox();
  }
  else
  {
    if (c.isQueryEmpty())
    {
      loupe_path.out(loupe_elm);
      c.closeSearchBox();
    }
    else
    {
      c.submitQuery();
    }
  }
}


Controller.prototype.enhanceSwap = function (jq, onlyObj)
{
  var only = onlyObj & true;
  var dict = this.dictionary;
  jq.each(
    function (n, obj)
    {
      dict.set(obj, new ImagePath(obj.src));
    }
  );
  if (!only)
  {
    jq.bind('mouseenter', this.swapImageOnHover);
    jq.bind('mouseleave', this.swapImageOnBlur);
  }
}

Controller.prototype.swapImageOnHover = function (ev)
{
  var c = Controller.getInstance();
  var img = ev.target;
  var pathinfo = c.dictionary.get(img);
  pathinfo.hover(img);
}

Controller.prototype.swapImageOnBlur = function (ev)
{
  var c = Controller.getInstance();
  var img = ev.target;
  var pathinfo = c.dictionary.get(img);
  pathinfo.out(img);
}


// STATIC MEMBERs/METHODS _________________________________________________

/*
Controller.navMap =
{
  'home': [],
  'stories': ['playyourbest', 'letters', 'behindthescene', 'submityourstory'],
  'clubs': ['drivers', 'fairwaywoods', 'hybrids', 'irons', 'wedges', 'putters', 'womens', 'juniors'],
  'customfitting': ['webfit', '5steps', 'fittingcartprogram', 'fittingglossary', 'fittingfaq'],
  'tour': ['pronews', 'pingpros', 'solheimcup', 'collegegolfguide'],  
  'bags': ['carrybags', 'cartbags', 'accessories'],
  'gear': ['apparel', 'headwear', 'gloves', 'oncoursegear', 'travelgear', 'dencaddy'],
  'community': [],
  'aboutping': ['timeline', 'communityreach', 'careers', 'downloads', 'tvspots', 'iso9001', 'contactus']
}
*/

Controller.cookie = {};

Controller.cookie.enabled = function()
{    
	// determines if browser can use cookies
    if (navigator.cookieEnabled != undefined) 
		return navigator.cookieEnabled; // Use navigator.cookieEnabled if this browser defines it
    
    if (Controller.cookie.enabled.cache != undefined) 
		return Controller.cookie.enabled.cache; // If we've already cached a value, use that value

    Controller.cookie.create('testCookie', 'test', 1); // create a test cookie
	
	var testCookie = Controller.cookie.read('testCookie'); // now see if that cookie was saved
	
	Controller.cookie.erase('testCookie'); // delete cookie;
    
	return Cookie.enabled.cache = (testCookie != null ? true : false); // cache the result and return it
}


Controller.cookie.create = function (name, value, days, path) 
{
	//console.log([name, value, days, path]);
	if (typeof(name) == 'undefined')
	{	throw new Error('Controller.js error: attempting to set a cookie without a name.');
	}
	var oldValue = Controller.cookie.read(name);
	
	if (days) 
	{	var date = new Date();
		date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
		var expires = "; expires=" + date.toGMTString();
	}
	else 
	{	var expires = "";
	}
	if (path)
	{	var realm = "; path=" + path;
	}
	else
	{	var realm = "; path=/";
	}
	document.cookie = name + "=" + escape(value) + expires + realm;
  
	/*
	// debug
	if(value == "")
	{	console.log("cookie '" + name + "' (" + value + ") deleted");
	}
	else
	{	console.log("cookie '" + name + "' (" + value + ") created");
	}
	*/	
}


Controller.cookie.read = function (name) 
{
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for (var i = 0; i < ca.length; ++i) 
  {
    var c = ca[i];
    while (c.charAt(0) == ' ')
    {
      c = c.substring(1, c.length);
    }
    if (c.indexOf(nameEQ) == 0)
    {
      return unescape(c.substring(nameEQ.length, c.length));
    }
  }
  return null;
}


Controller.cookie.erase = function (name) 
{
  Controller.cookie.create(name, "", -1);
}

Controller.cookie.create('lastVisit', new Date().getTime(), 365);
// can use UTC and TimezoneOffset to identify region/daylight savings - measured in minutes
/*	Zone	City or Area Zone Crosses
	GMT-12	Eniwetok
	GMT-11	Samoa
	GMT-10	Hawaii
	GMT-9 	Alaska
	GMT-8 	PST, Pacific US
*	GMT-7 	MST, Mountain US
	GMT-6 	CST, Central US
	GMT-5 	EST, Eastern US
	GMT-4 	Atlantic, Canada
	GMT-3 	Brazilia, Buenos Aries
	GMT-2 	Mid-Atlantic
	GMT-1	Cape Verdes
	GMT 	Greenwich Mean Time, Dublin
	GMT+1	Berlin, Rome
	GMT+2 	Israel, Cairo
	GMT+3 	Moscow, Kuwait
	GMT+4 	Abu Dhabi, Muscat
	GMT+5 	Islamabad, Karachi
	GMT+6 	Almaty, Dhaka
	GMT+7 	Bangkok, Jakarta
	GMT+8 	Hong Kong, Beijing
	GMT+9 	Tokyo, Osaka
	GMT+10	Sydney, Melbourne, Guam
	GMT+11	Magadan, Soloman Is.
	GMT+12 	Fiji, Wellington, Auckland
*/
	

// firstborn: June 26 2008
// ________________________________________________________________________
Controller.defer = function (jq, func)
{
  // check arguments
  if (!(jq instanceof jQuery)) throw new Error('1st arg must be jQuery');
  if (typeof(func) != 'function') throw new Error('2nd arg must be a function');

  // variable statements
  var arr, finishAll, checkFinish, initilizeImg, i;
  arr = [];
  finishAll = function (){func();}

  // construct private functions
  checkFinish = function ()
  {
    var j;
    for (j = 0; j < arr.length; ++j)
    {
      if (!arr[j]) return;
    }
    finishAll();
  }
  initilizeImg = function (elm, idx)
  {
    elm.finish = function ()
    {
      arr[idx] = true;
      checkFinish();
    }
    elm.onload = function (ev)
    {
      this.onload = null;
      this.onerror = null;
      this.finish();
    }
    elm.onerror = function (ev)
    {
      if ((typeof(this.src) == 'undefined') || (this.src != ''))
      {
        this.onload();
      }
    }
  }

  // start process codes
  for (i = 0; i < jq.length; ++i)
  {
    if (jq.get(i).complete)
    {
      arr[i] = true;
    }
    else
    {
      arr[i] = false;
      initilizeImg(jq.get(i), i);
    }
  }
  checkFinish();
}
