var Assemblies = {};
var Prelude;
var SearchPaths;
var Initialized = false;
var TypeDefType = null;
var OnServer = IsOnServer();
var TypeParameters = { TypeRefs : {} };
//var PATH = "jsfile.aspx?a=";
var PATH = "http://hello/content/";

function IsOnServer()
{
    if (typeof(window) == "undefined") return false;
    if (typeof(document) == "undefined") return false;
    if (document.location.host != "") return true;
    return false;
}
function GetTypeDef(assembly, name)
{
    var lookup = assembly.TypeDefs[name];
    if (lookup != undefined)
    {
        lookup._vT = TypeDefType;
        return lookup;
    } 
//    if (OnServer)
//    {
//        LoadCode(PATH + encodeURIComponent(assembly.Name) + "&t=" + encodeURIComponent(name)); 
//    }
//    else 
    {
        var shortName = assembly.TypeMappings[name];
        if (shortName == null)
        {
            var result = confirm("Failed to load type'" + name +"'\nWould you like to debug?");
            if (result) debugger;
        }
        LoadCode(assembly.Name +"/Types/" + shortName + ".js"); 
    }
    lookup = assembly.TypeDefs[name];
    if (lookup != undefined)
    {
        if (typeof(lookup)!="function")
        {        
            lookup.TypeInitializer(lookup);
        }
        lookup._vT = TypeDefType;
        return lookup;
    }
    var result = confirm("Failed to load type'" + name +"'\nWould you like to debug?");
    if (result) debugger;
    assembly.TypeDefs[name] = null;
    return null;
}

function GetTypeRef(assembly, name)
{
    var lookup = assembly.TypeRefs[name];
    if (lookup != undefined)
    {
        return lookup;
    }
    lookup = NewTypeRef(assembly, name);
    assembly.TypeRefs[name] = lookup;
    return lookup;
}

function NewTypeRef(assembly, name)
{
  return { Assembly : assembly, Id : GetUniqueName("tr"), Name : name, MethodRefs : {}, Instances :[], Arrays : {} };
}

function GetField(typedef, name)
{
    return typedef.PublicFields[name];
}

function ToDef(typeref)
{
  if (typeref.TypeArguments != null)
  {
     return GetInstance(typeref, typeref.Name, typeref.TypeArguments);
  }
  return GetTypeDef(typeref.Assembly, typeref.Name);
}


function ToRef(typedef)
{
  //array support is implemented in managed code
  if (typedef.ToRef != null) return typedef.ToRef();
  var ref = GetTypeRef(typedef.Assembly, typedef.Name);
  if (typedef.TypeArguments != null)
  {
     return GetInstanceRef(ref, typedef.TypeArguments);
  }
  return ref;
}

function ReferenceTo(typeref)
{
  if (typeref.Reference != null) return typeref.Reference;
  typeref.Reference = NewTypeRef(typeref.Assembly, typeref.Name +"@");
  return typeref.Reference;
}

function LoadAssembly(assembly)
{
    if (!Initialized)
    {
        Initialized = true;
        Initialize();
    }
    var lookup = Assemblies[assembly];
    if (lookup != undefined)
    {
        return lookup;
    }
//    if (OnServer)
//    {
//        alert('on server');
//        LoadCode(PATH + encodeURIComponent(assembly)); 
//    }
//    else
    {
        LoadCode(assembly+"/assembly.js");
        LoadCode(assembly+"/typeMappings.js");
    }
    lookup = Assemblies[assembly];
    if (lookup != undefined)
    {
        lookup.RegisterExports();
        return lookup;
    }
    Assemblies[assembly] = null;
    return null;
}

var xmlhttp = null;
var LoadCode = null;
var LoadCodeFromSearchPath = null
function DetectLoadMechanism()
{
    if (typeof voltaTestAPI != "undefined")
    {
        LoadCode = LoadCodeUsingExternal;
    }
    else if (!OnServer)
    {
        LoadCode = LoadCodeWithSearchPath;
        if (LoadCodeFromSearchPath == null)
        {
            LoadCodeFromSearchPath = LoadCodeUsingXmlHttp;
            xmlhttp = GetXmlHttp();
        }
    }
    else
    {
        LoadCode = LoadCodeUsingXmlHttp;
        xmlhttp = GetXmlHttp();
    }
}

function GetXmlHttp()
{
    if (typeof ActiveXObject!= "undefined")
    {
        return new ActiveXObject("MSXML2.XMLHTTP");
    }
    else if (typeof XMLHttpRequest != "undefined")
    {
        return new XMLHttpRequest();
    }
    return null;
}

function LoadCodeWithSearchPath(file)
{
    if (SearchPaths == null) SearchPaths = ["."];
    var done = false;

    if (LoadCodeFromSearchPath)
    {
        for(var i =0; i < SearchPaths.length; i++)
        {    
            if (LoadCodeFromSearchPath(SearchPaths[i]+"/"+file))
            {
                done = true;
                break;
            }
        }
    }

    if (!done)
    {
        var result = confirm("Failed to download '" + file +"\nWould you like to debug?");
        if (result) debugger;
    }
}

function LoadCodeUsingExternal(file)
{
    var code = window.external.LoadCode(file);
    eval(code);
    return true;
}

function LoadCodeUsingXmlHttp(file)
{
    //alert('file: ' + file);
    try
    {
        xmlhttp.open("GET", file, false);
        xmlhttp.send(null);
    }
    catch(ex)
    {
        alert('exception: ' + ex);
        return false;
    }

    if (xmlhttp.status==200 || xmlhttp.status==0)
    {
        var response = xmlhttp.responseText;
        //alert('response: ' + response);
        if (response == null || response == "")
        {
            return false;
        }
        eval(response);
        return true;
    }
    return false;
}

function GetMethodDef(typedef, name, types)
{
    var typesig = GetSignature(types);
    var methods = typedef.PublicMethods[name];
    var methodName = methods[typesig];
    return typedef.Methods[methodName];
}

function GetInstanceRef(typeref, types)
{
    for(var i=0; i < typeref.Instances.length; i++)
    {
        var instance = typeref.Instances[i];
        var signature = instance[0];
        if (SignaturesMatch(types, signature))
            return instance[1];
    }
    var nameTokens = [];
    for(var j=0;j<types.length; j++)
    {
        nameTokens.push(types[j].Name);
    }
    var name = typeref.Name +"<" + nameTokens.join(",") +">";
    var newTypeRef = NewTypeRef(typeref.Assembly, name);
    typeref.Instances.push([types, newTypeRef]);
    return newTypeRef;
}

function GetInstanceDef(assembly, name, types)
{
    var listInstances = assembly.Instances[name];
        if (listInstances != null)
    {
        for(var i=0; i < listInstances.length; i++)
        {
            var mappings = listInstances[i];
            var signature = mappings[0];
            if (SignaturesMatch(types, signature))
                return mappings[1];
        }
    }
    else
    {
        listInstances = [];
        assembly.Instances[name] = listInstances;
    }
    var template = GetTypeDef(assembly, name);
    var instance = template.apply(null, types);
    listInstances.push([types, instance]);    
    instance.TypeInitializer(instance);
    return instance;
}

function GetArrayRef(typeref, rank)
{
    var cached = typeref.Arrays[rank];
    if (cached != null) return cached;

    var rankName = GetRankName(rank);

    var arrayTypeRef = { Id : typeref.Id + rankName, ElementType : typeref, Rank : rank, Arrays : {} };
    typeref.Arrays[rank] = arrayTypeRef;
    return arrayTypeRef;
}

function GetRankName(rank)
{
    var rankTokens = ["["];
    for(var i=1; i < rank; i++)
    {
        rankTokens.push(",");
    }
    rankTokens.push("]");
    return rankTokens.join("");
}

function GetMethodRef(typeref, name, types)
{
    var typesig = GetSignature(types);
    var methods = typeref.MethodRefs[name];
    if (methods == null)
    {
        methods = {};
        typeref.MethodRefs[name] = methods;
    }
    else
    {
        var item = methods[typesig];
        if (item != null) 
        {
            return item;
        }
    }
    var mr = GetUniqueName("mr");
    methods[typesig] = mr;
    return mr;
}

function GetSignature(types)
{
    var sigTokens = [];
    for(var i=0; i < types.length; i++)
    {
        sigTokens.push(types[i].Id);
    }
    return sigTokens.join(",");
}


function SignaturesMatch(first, second)
{
    if (first.length != second.length) return false;
    for(var i=0; i < first.length; i++)
    {
        if (first[i] != second[i]) return false;
    }
    return true;
}

var runtimeCounter = 0;
function GetUniqueName(prefix)
{
    return prefix + (runtimeCounter++);
}

function NSErr(debug, what)
{
    var msg = "Cannot use '"+what+"' since the 'NotSupported' attribute has been applied to it thus preventing its definition from being compiled by the volta compiler.";
    CreateErrorInternal(debug, msg);
}

function NSIErr(debug, origMethod, newMethod)
{
    var msg = "Method '"+origMethod+"' cannot be overridden/implemented by '"+newMethod+"'"
    CreateErrorInternal(debug, msg);
}

function CreateErrorInternal(debug, msg)
{
    if (debug == 1)
    {
        var dbgMsg = msg + "\n\nCallstack:\n" + DebugPrintCallStack() + "\n\nWould you like to debug?";
        var result = confirm(dbgMsg);
        if (result) 
        {
            debugger;
        }
        else
        {
            CreateError(msg);
        }
    }
    else
    {
        CreateError(msg);
    }
}

function DebugPrintCallStack()
{
    try
    {
        var arrCallStack = new Array();
        var objStartOfCallstack = DebugGetCallingFunction(null);

        DebugBuildCallStack(objStartOfCallstack, arrCallStack);

        return arrCallStack.join("\n");
    }
    catch (e)
    {
        return "Error retreiving callstack:\n\t" + e.message;
    }
};

function DebugBuildCallStack(objCallingFunction, arrCallStack)
{
    if (objCallingFunction && objCallingFunction.callee)
    {
        var strSource = objCallingFunction.callee.toString().match(/function\s*(.*)\(/)[1];
        if (strSource == null || strSource == "")
        {
            strSource = "[Anonymous function]";
        }
        arrCallStack.push(strSource);
        DebugBuildCallStack(DebugGetCallingFunction(objCallingFunction), arrCallStack);
    }
    else 
    {
        arrCallStack.push("<End>");
    }
};

function DebugGetCallingFunction(objFunction)
{
    if (objFunction == null)
    {
        return arguments.caller;
    }
    else if (objFunction.caller)
    {
        return objFunction.caller;
    }
    else if (objFunction.arguments && objFunction.arguments.caller)
    {
        return objFunction.arguments.caller;
    }
    else
    {
        return null;
    }
};

function CreateError(error)
{
    alert(error);
    throw error;
}

function Initialize()
{
    DetectLoadMechanism();

    Prelude = LoadAssembly("VoltaPrelude, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
    TypeDefType=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.Prelude.TypeDef");
    String.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.String");
    Date.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.Date");
    Function.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.Function");
    Number.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.Number");
    Boolean.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.Boolean");
    Error.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.Error");
    TypeError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.TypeError");
    RangeError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.RangeError");
    EvalError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.EvalError");
    SyntaxError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.SyntaxError");
    ReferenceError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.ReferenceError");

    if (typeof UriError != "undefined")
    {
        UriError.prototype._vT=GetTypeDef(Prelude, "Microsoft.LiveLabs.Volta.JavaScript.UriError");
    }

}

function LoadCodeUsingScript(name)
{
    var fullName = SearchPaths[0] +"/" + name;
    document.write("<script src=\"" + fullName + "\"></script>");
}

function LoadStrongType(strongName)
{
    if (strongName == undefined || strongName == null) return null;
    var commaIndex = strongName.indexOf(",");
    var typeString = strongName.substring(0, commaIndex);
    var assemblyString = strongName.substring(commaIndex + 2);
    var assembly = LoadAssembly(assemblyString);
    return GetTypeDef(assembly, typeString);
}