
var SCOPE_SIZE = 40; 
//var ScopeArray = new Array(SCOPE_SIZE);
//var ExchDisplayArray = new Array(SCOPE_SIZE);
//var ScopeArray = new Array();
var ExchDisplayArray = new Array();
var CACHE_SIZE = 10;
var MAX_SHOWN = 12;
var JRequests = new Array();
var KEY_DELIMITER = ',';
var LOCAL_SYMBOL_TICKER_POS = 1;
var IDX_BASE = 1024;
var NAME_IDX_BASE = IDX_BASE * 2;
//var AllCache = new Array(SCOPE_SIZE);
//var NextCachePos = new Array(SCOPE_SIZE);
  
//function initCache(cache, nextCachePos)
//{
//    for (var i=0; i < cache.length; i++) 
//    {
//        cache[i] = null;
//        nextCachePos[i] = 0;
//    }    
//    //alert('init cache done.');
//}
	
function leftTrim(str)
{
    //alert('left')
    a = str.replace(/^\s+/, '');
    // return a.replace(/\s+$/, '');
    return a;
}

function JSONscriptRequest(fullUrl, jsid) 
{
    // REST request path
    this.fullUrl = fullUrl; 
    // Keep IE from caching requests
    this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
    // Get the DOM location to put the script tag
    this.headLoc = document.getElementsByTagName("head").item(0);
    // Generate a unique script tag id
    this.scriptId = jsid;
}

// Static script ID counter
JSONscriptRequest.scriptCounter = 1;

// buildScriptTag method
//
JSONscriptRequest.prototype.buildScriptTag = function () 
{

    // Create the script tag
    this.scriptObj = document.createElement("script");
    
    // Add script object attributes
    this.scriptObj.setAttribute("type","text/javascript");
    this.scriptObj.setAttribute("charset","utf-8");
    this.scriptObj.setAttribute("src",this.fullUrl+this.noCacheIE);
    this.scriptObj.setAttribute("id",this.scriptId);
}
 
// removeScriptTag method
// 
JSONscriptRequest.prototype.removeScriptTag = function () 
{
    // Destroy the script tag
    this.headLoc.removeChild(this.scriptObj);  
}

// addScriptTag method
//
JSONscriptRequest.prototype.addScriptTag = function () 
{
    // Create the script tag
    this.headLoc.appendChild(this.scriptObj);
}

function Record(rkey, rpos, leaf, data)
{
    this.key = rkey;
    this.startPos = rpos;
    this.isLeaf = leaf;
    this.data = data;
}

// if MatchPos < 255, it's ticker match starting from MatchPos
// if MatchPos > 255, it's name match starting from (MatchPos - 255);
function DataEntry(symbol, name, region, id, cfid, type, info, exchange, pos, key2, primaryKeys)
{
    this.Symbol = symbol;
    this.Name = name;
    this.Exchange = exchange;
    this.ID = id;
    this.CFID = cfid;
    this.Type = type;
    this.Info = info;
    this.Region = region;
    this.MatchPos = pos;
    this.Key2 = key2;
    this.PrimaryKeys = primaryKeys;
}

function ACQResult(key, values)
{
    this.Key = key;
    this.Values = values;
}

//function AutoCompleteOracle(defaultScope, myCache, myCachePos, myID, myDisplayCallback)
function AutoCompleteOracle(myCache, myCachePos, myID, myDisplayCallback, myOwner)
{ 
  var ACQ_ROW_DELIMITER = "}]";
  var ACQ_COL_DELIMITER = "[|";
  var ACQ_SYMBOL_DELIMITER = ";";
  
  var HEADER_ROWS = 4; 
  var Current_Value = "";
  var HIDE_DELAY_MS = 200;
  var AllCache = myCache;
  var NextCachePos = myCachePos;

  var REFOCUS_DELAY_MS = 50;
  
  var ENTRY_SELECT_COLOR = '#B1D9F0';
  var ENTRY_COLOR = 'white';
	 
  var EXCH_DELIMITER = ':';
  
  var IgnoreFuncKey = false;
  var IgnoreAppleArrowKey = false;

//  var KeyScopeStr = '';
//  var DefaultScope = 4;
  
  
//  if(defaultScope>=0 && defaultScope<SCOPE_SIZE)
//  {
//	 DefaultScope = defaultScope;				
//  }
//  
//  var CurScope = DefaultScope;
//  var KeyScope = DefaultScope;
  
  var ac_self = this;
  var IsSubmit = true;  

  this.DisplayCallback = myDisplayCallback;
  var Owner = myOwner;
  this.NumShownEntries = 0;
  this.ShownEntries = new Array(MAX_SHOWN);  

  var Region = 'USA';
  var PreferredLanguage = 'EN';
  var SecurityType = '';
  this.ProxyWrap = null;
  
  this.ACQ_SERVER='//qt.morningstar.com'; 
  //this.ACQHandlerURL = this.ACQ_SERVER + "/acindex/acq.ashx?callback=AutoCompleteOracle.FCallBack"+myID+"&out=j";	  
  var boxID = myID;
  this.ACQHandlerURL = this.ACQ_SERVER + "/gidindex/acq.ashx?callback=AutoCompleteOracle.FCallBack"+boxID+"&out=j";
  
  var LocalStockSymbolPref = new Array(1,2,0);
  var LocalOtherSymbolPref = new Array(1,2,0);

    
  (function(){
        var funcString = "AutoCompleteOracle.FCallBack"+boxID+" = function(p){ac_self.j_callback(p);}";
        eval(funcString);
    })();
   
  this.SetACQServer = function(server)
  {
    this.ACQ_SERVER = server;
    this.ACQHandlerURL = this.ACQ_SERVER + "/gidindex/acq.ashx?callback=AutoCompleteOracle.FCallBack"+boxID+"&out=j";
  };

  this.SetPreference = function(reg, language, sec_type)
  {
    if (reg == null || language == null || sec_type == null)
        return; // ignore it.
        
    if (Region != reg) {
        Region = reg.toUpperCase();
        switch (Region)
        {
        case 'USA':
        case 'CAN':
            LocalStockSymbolPref = new Array(1,2,0);
            LocalOtherSymbolPref = new Array(1,2,0);
            break;
        case 'CHN':
            LocalStockSymbolPref = new Array(1,0,2);
            LocalOtherSymbolPref = new Array(1,0,2);
            break;
        default:
            LocalOtherSymbolPref = new Array(1,0,2);
            LocalOtherSymbolPref = new Array(0,1,2);
            break;
        }
    }
    PreferredLanguage = language.toUpperCase();
    SecurityType = sec_type;
    Current_Value = '';
  };
  
  function getCurrentKey(myKey)
	{
	    var key;
	    var wholekey;
	    var start = 0;
	    try 
	    {
	        wholekey = myKey;
	        
	        start = wholekey.lastIndexOf(KEY_DELIMITER);
	
	        if (start >= wholekey.length)
	        {
	            key = '';
	        } else {
	            start++;
	            key = wholekey.substr(start);
	            key = leftTrim(key);
	        }
	        //alert(key);
	    } 
	    catch (ae)
	    {
	        key = '';
	    }
	    
	    return key;    
	}
	
    function SendResult(key)
    {
        var callback_result = new ACQResult(key, ac_self.ShownEntries);
        if (Owner != null) {
            Owner.ProcessResult(callback_result);
        }
        else if (ac_self.DisplayCallback != null)
        {
            if (ac_self.NumShownEntries == 0)
            {
                callback_result.Values = null;
            }
            var funcstr = ac_self.DisplayCallback + '(callback_result);';
            eval(funcstr);
        }            
    }
    
    this.SendQuery = function (myKey)
	{ 
	    var key = '';
	    var ret = false;
	    	    
	    key = getCurrentKey(myKey);    	
	    key = key.toLowerCase();
	        
	    if (key == Current_Value)
	    {
            SendResult(key);
            return ret;
	    }	    
	    
	    if (key.length == 0) {
	        Current_Value = "";
	        resetShownEntries();
	        SendResult(key);
	        return ret; // ret;
	    }
		
	    Current_Value = key;
	    
	    // check cache.
	    var cache_hit = 1;
	    //var sc_str = 's' + CurScope;
	    var sc_str = Region + '_' + SecurityType;
	    if (AllCache[sc_str] != null) {
	        var Cache = AllCache[sc_str];
	        for (var i = 0; i<CACHE_SIZE; i++) {
	            cache_hit = AutoCompleteOracle.isRecordHit(key, Cache[i]);
	            if (cache_hit == 0) {
	                //alert('cache hit');
	                ac_self.displayRecord(Cache[i]);
	                return ret;// ret;
	            }
	        }
	    }
		var jsid = 'JD' + JSONscriptRequest.scriptCounter;

	    JSONscriptRequest.scriptCounter++;
	    //var cope = CurScope;
	    //var url = ac_self.ACQHandlerURL+"&sc="+cope+"&key=" + encodeURIComponent(key) + "&js="+jsid;
	    var url = ac_self.ACQHandlerURL+'&reg='+Region+'&key=' + encodeURIComponent(key) + '&js='+jsid + '&range=' + SecurityType;
	    var Req = null;
	    if (this.ProxyWrap == null) {
	        Req = new JSONscriptRequest(url, jsid);
	    } else {
	        Req = new JSONscriptRequest(this.ProxyWrap(url), jsid);
	    }
	    //alert('about to call ' + url);    
	    Req.buildScriptTag();
	    JRequests[JRequests.length] = Req; // append to the request queue
	    Req.addScriptTag();  // call it.
	
	    return ret;
	};	
		
	function resetShownEntries()
	{
	    for (var i=0; i<MAX_SHOWN; i++)
	    {
	        ac_self.ShownEntries[i] = null;
	    }
	    
	    ac_self.NumShownEntries = 0;
	    
	    //alert('ShownEntries reset');
	}
	
	ac_self.displayRecord = function(record)
	{
	    //alert('display record');	    
	    try 
	    {
                resetShownEntries();
	            if (record != null)
	            {
	                generateContentFromRecord(Current_Value, record);
	            }
	            SendResult(Current_Value);	           
	            //alert('here');				        
	    } 
	    catch (e) 
	    {
	        alert(e);
	    }
	};
    
    function generateContentFromRecord(key, record)
    {
        //alert('generateContentFromRecord called');
        var content = "";
        if (record.data == null) 
        {
            return content;
        }
        var size = record.data.length;
        if (size == 0) 
        {
           return content;
        }
	    
        var tmpstr = "";
        var partial = "";
	
        for (var i=0; i<size; i++) 
        {
            generateContentForRow(key, record.data[i]);		            
            if (ac_self.NumShownEntries >= MAX_SHOWN) 
            {
                break;
            }			        
       }
    }

//  function checkKeyMatch(key, value)
//  {
//    var pos = -1; // no match
//    if (value.length == 0)
//    {
//        return pos;
//    }
//    
//     var tmp = value.toLowerCase();
//     pos = tmp.indexOf(key, 0);
//     return pos;
//  }

  function checkKeyMatch(key, symbols, type)
  {
    var ret = new Array(3);
    ret[0] = -1;
    ret[1] = '';
    ret[2] = '';
    if (symbols.length == 0)
    {
        return ret;
    }
        
    var size = symbols.length;
    var pos = -1;
    if (size >= 3)
    {
        // check primary key first
        checkPrimaryKey(key, symbols, type, ret);
        if (ret[0] < 0)
        {
            // check secondary key
            checkSecondaryKey(key, symbols, ret);
        }
    }
    
    return ret;
  }
  
  function checkSecondaryKey(key, symbols, result)
  {
    // not found in primary key
    var size = symbols.length;
    for (var i = 3; i < size; i++)
    {
         var tmp = symbols[i].toLowerCase();
         pos = tmp.indexOf(key, 0);
         if (pos >= 0)
         {
            result[2] = symbols[i];
            result[0] = pos + IDX_BASE;
            break;
         }
    }
  }
  
  function checkPrimaryKey(key, symbols, type, result)
  {
    var idxes;
    var backup = '';
    var pos = -1;
    if (type == 'FE' || type == 'ST' || type == 'E0' || type == 'E1')
    {
        idxes = LocalStockSymbolPref;
    } else {
        idxes = LocalOtherSymbolPref;
    }
    for (var i = 0; i < 3; i++)
    {
        var cur_symbol = symbols[idxes[i]];
        var tmp = cur_symbol.toLowerCase();
        if (cur_symbol != '')
        {
            if (backup == '') {
                backup = cur_symbol;
            }
            pos = tmp.indexOf(key, 0);
            if (pos >= 0)
            {
               result[1] = cur_symbol;
               break;
            }

        }
    }
    if (pos < 0)
    {
        result[1] = backup;
    }

    result[0] = pos;   
  }
  
  function generateContentForRow(key, row)
  {
	    //alert('key ' + key + '   row ' + row);
	    var ret = "";
	    var cells = row.split(ACQ_COL_DELIMITER);
	    var found_match = false;
	    var tmp;
	    var pos; 
	    var MAX_MATCH_IDX = 7;
	    var SYMBOL_POS = 6;
	    var NAME_POS = 7;
	    var EXCH_POS = 1;
	    var REGION_POS = 2;
	    var INFO_POS = 5;
	    var TYPE_POS = 3;
	    var replaced = false;
	    //var NAME_POS_BASE = 255;
	    var symbol = '';
	    var final_name = '';
	    var key2 = '';
	    if (cells.length >= MAX_MATCH_IDX) 
	    {
	        var all_symbols = cells[SYMBOL_POS].split(ACQ_SYMBOL_DELIMITER);
	        var names = cells[NAME_POS];

//            pos = checkKeyMatch(key, symbol);
//	        if (pos >= 0) {
//	            found_match = true;
//	        }
	        var key_result = checkKeyMatch(key, all_symbols, cells[TYPE_POS]);
	        if (key_result[0] >= 0)
	        {
	            found_match = true;
	            pos = key_result[0];
	        }
	        symbol = key_result[1];
	        key2 = key_result[2];
	        all_symbols = all_symbols.slice(0,3); // get primary keys only
	        if (!found_match) {
                var result = FindMatchingName(key, PreferredLanguage, names);
                if (result[0] >= 0)
                {
                    found_match = true;
                    pos = result[0] + NAME_IDX_BASE;
                    final_name = result[1];
                }
	        } else {
	            // look for a name.
	            final_name = FindName(PreferredLanguage, names);
	        }
	        
	        if (found_match) {
	            // For Stock:
                // PerformanceID  Exchange    Region  Type    CompanyID   Currency    Keys    Name
                // For Others:
                // ShareClassID   Exchange    Region  Type    FundID  [Currency,PerformanceID;]   Keys    Name
                
              if (symbol=='')
              {
                if (key2 == '') {
                    symbol = cells[0]; // use shareclassID
                } else {
                    symbol = key2;
                    key2 = '';
                }
              }
              var info = cells[INFO_POS];
              var type = cells[TYPE_POS];
              if (type == 'ST' || type == 'E0' || type == 'E1')
              {
                info += (',' + cells[0]);
              }
	          ac_self.ShownEntries[ac_self.NumShownEntries] = 
	            new DataEntry(symbol, final_name, cells[2], cells[0]
	                , cells[4], type, info, cells[1], pos
	                , key2, all_symbols
	                );
	            
	          ac_self.NumShownEntries++;
	        }
        }
	    return ret;
	}
	
	this.processResult =  function(result)
	{
        var record;
	    var rows = result.split(ACQ_ROW_DELIMITER);
	    record = new Record("", 0, 'true', null);
	    
	    //alert(rows.length);
	    if (rows.length >= HEADER_ROWS) 
	    {
	        // the first row is the corresponding key
	        record.key = rows[0];	        
	        // scope
	        var sc = rows[1];
	        // start position
	        record.startPos = rows[2];
	        // leaf node?
	        record.isLeaf = rows[3];
	        
	        // get data.
	        record.data = null;
	        var size = rows.length - HEADER_ROWS;
	        if (size > 0) 
	        {
	            record.data = new Array(size);
	            for (var i=HEADER_ROWS; i<rows.length; i++)
	            {
	                record.data[i - HEADER_ROWS] = rows[i]; 
	            }
	        }
	        
	        var sc_str = sc;        
	        // put it into the cache.
	        if (record.data != null)
	        {
	            if (AllCache[sc_str] == null) 
	            {
	               AllCache[sc_str] = AutoCompleteOracle.createCacheForScope();
	               NextCachePos[sc_str] = 0;
	            }
	            var Cache = AllCache[sc_str];
	            Cache[NextCachePos[sc_str]] = record;
	            NextCachePos[sc_str]++;
	            if (NextCachePos[sc_str] == CACHE_SIZE) 
	            {
	                NextCachePos[sc_str] = 0;
	            }
	        }
	        
	        // display it if needed.

	            var hit = AutoCompleteOracle.isRecordHit(Current_Value, record);
	            if (hit == 0) 
	            {
	                ac_self.displayRecord(record);
	            }
	    }	  
	};

    //
	// Constructor
	//
	{ 
        //initExchArray();
        resetShownEntries();
	}
}


AutoCompleteOracle.prototype.j_callback = function(resultset)
{
    //alert('callback');
  
    var jdata = resultset.ResultSet;
    if (jdata.result.length > 0) 
    {
        //this.processResult(jdata.sc, jdata.result);
        this.processResult(jdata.result);
    }
    
    // remove the request node.
    var Req = null;
    for (var i = 0; i<JRequests.length; i++)
    {
        if (JRequests[i] != null) 
        {
            //alert(JRequests[i].scriptId + ' : ' + jdata.jsid);
            if (JRequests[i].scriptId == jdata.jsid)
            {
                Req = JRequests[i];
                break;
            }
        }
    }
    
    if (Req != null) 
    {
        //alert('found');
        JRequests.splice(i,1); // remove this entry.
        Req.removeScriptTag();
    }
};

AutoCompleteOracle.isRecordHit = function(key, record)
{
    var pos;
    if (record == null) 
    {
        return 1; // not hit.
    }
    if (key.length >= record.key.length) 
    {
        if (key == record.key) 
        {
            return 0; // hit
        }
        pos = key.indexOf(record.key);
        if (pos == 0) 
        {
            if (record.isLeaf == 1)
            {
                return 0; // hit because this is a leaf for the same prefix
            }
            return 1; // no hit.
        }
        return 1; // no hit
    } 
    else 
    {
        if (key.length < record.startPos) 
        {
            return 1; // no hit
        }
        // key is shorter
        pos = record.key.indexOf(key);
        if (pos == 0) 
        {
            return 0; // hit
        }
        return 1; // no hit
    }
};

AutoCompleteOracle.createCacheForScope =  function()
{
    var Cache = new Array(CACHE_SIZE);
    for (var c = 0; c < CACHE_SIZE; c++)
    {
        Cache[c] = null;
    }
    return Cache;
};

    function StringWithCulture(culture, str)
    {
        this.Culture = culture;
        this.Data = str;
    }

    function MakeNameArrayWithLanguage(allNames)
    {
        var NAME_DELIMITER = ';';
        var FIELD_DELIMITER = '[';
        
        var ret = new Array();
        var tmp_names = allNames.split(NAME_DELIMITER);
        var size = tmp_names.length;    
        for (var i = 0; i < size; i++)
        {
            var fields = tmp_names[i].split(FIELD_DELIMITER);
            if (fields.length == 2)
            {
                var swc = new StringWithCulture(fields[0], fields[1]);
                ret.push(swc);
            }
        }
        
        return ret;
    }

    function FindName(lang, allNameStr)
    {
        var default_lang='EN';
        var my_parent = GetParentCulture(lang);
        var parent_result = null;
        var sibling_result = null;
        var default_result = null;
        var allNames = MakeNameArrayWithLanguage(allNameStr);
        var size = allNames.length;        
        for (var i=0; i<size; i++)
        {
            var cul = allNames[i].Culture;
            var name = allNames[i].Data;
            
            if (cul == lang)
            {
                // exact match
                return name;
            } 
            else if (cul == my_parent)
            {
                parent_result = name;
            }
            else if (cul == default_lang)
            {
                default_result = name;
            } 
            else
            {
                var tmp_parent = GetParentCulture(cul);
                if (tmp_parent == my_parent)
                {
                    sibling_result = name;
                }
            }
        }
        if (parent_result != null)
        {
            return parent_result;
        }
        if (sibling_result != null)
        {
            return sibling_result;
        }
        if (default_result != null)
        {
            return default_result;
        }
        return '';
    }
    function FindMatchingName(key, lang, allNameStr)
    {
        var default_lang='EN';
        var my_parent = GetParentCulture(lang);
        var found_match = false;
        var cur_match_name = '';
        var parent_result = null;
        var sibling_result = null;
        var default_result = null; 
        var parent_pos = -1;
        var sibling_pos = -1;
        var default_pos = -1;
        var ret = new Array(2);
        ret[0] = -1;
        ret[1] = '';
        var allNames = MakeNameArrayWithLanguage(allNameStr);
        var size = allNames.length;        
        for (var i=0; i<size; i++)
        {
            var cul = allNames[i].Culture;
            var name = allNames[i].Data;
            var fields = searchKeyInName(key, name);
            if (fields[0] < 0)
            {
                continue; // no match.            
            }
            
            if (cul == lang)
            {
                // exact match
                ret[0] = fields[0];
                ret[1] = fields[1];
                return ret;
            } else if (cul == my_parent)
            {
                parent_pos = fields[0];
                parent_result = fields[1];            
            } else if (cul == default_lang)
            {
                default_pos = fields[0];
                default_result = fields[1];
            } else
            {
                var tmp_parent = GetParentCulture(cul);
                if (tmp_parent == my_parent)
                {
                    sibling_pos = fields[0];
                    sibling_result = fields[1];
                }
            }
        }
        if (parent_result != null)
        {
            ret[0] = parent_pos;
            ret[1] = parent_result;
        }
        else if (sibling_result != null)
        {
            ret[0] = sibling_pos;
            ret[1] = sibling_result;
        }
        else if (default_result != null)
        {
            ret[0] = default_pos;
            ret[1] = default_result;
        }
        return ret;     
    }

    function searchKeyInName(key, name)
    {
      var ret = new Array(2);
      ret[0] = -1;
      ret[1] = '';
      var pos;
      if (name != null) {
        tmp = name.toLowerCase();
        pos = tmp.indexOf(key, 0);
        if (pos < 0) {
          // consider the situation of removing '.'
          tmp = tmp.replace(/\./g, '');
          pos = tmp.indexOf(key, 0);
          if (pos >= 0) {
            ret[1] = name.replace(/\./g, '');
          }
        } else {
          ret[0] = pos;
          ret[1] = name;
        }
        return ret;
      }
    }

    function GetParentCulture(lang)
    {
        if (lang.length == 2)
        {
            return lang;
        }
        if (lang.length < 2)
        {
            return '';
        }
        var ret = lang.substr(0,2);
        if (ret == 'ZH')
        {
            switch (lang)
            {
                case "ZH-TW":
                case "ZH-HK":
                case "ZH-MO":
                    ret = 'ZH-HANT';
                    break;
                default:
                    ret = 'ZH-HANS';
            }
        }
        return ret;
    }