import {PHPFunctionType, PHPSimpleType, PHPTypeUnion} from "./phptype"

export class FileContext {
    /**
     *
     * @param {string} filename
     */
    constructor(filename) {
        this.filename = filename
    }
}

/**
 * Defines content in a specific class
 */
class ClassContext {
    /**
     * Builds the object
     * @param {string} name Fully qualified only
     */
    constructor(name) {
        this.name = name
        this.staticIdentifiers = {}
        this.instanceIdentifiers = {}
    }
    /**
     * Adds a known identifier
     * @param {string} name
     * @param {string} scope "public", "private" or "protected"
     * @param {PHPTypeUnion} types
     * @param {boolean} is_static
     */
    addIdentifier(name, scope, is_static, types) {
        if(is_static) {
            this.staticIdentifiers[name] = {
                scope: scope,
                types: types,
            }
        } else {
            this.instanceIdentifiers[name] = {
                scope: scope,
                types: types,
            }
        }
    }
    /**
     * Finds the named identifier
     * @param {string} name
     * @param {?ClassContext} from_class_context
     * @returns {?PHPTypeUnion}
     */
    findInstanceIdentifier(name, from_class_context) {
        let m = this.instanceIdentifiers[name]
        if(m) {
            if(from_class_context === this || m.scope == "public") {
                return m.types
            }
            // TODO inheritance
        }
        return null
    }
    /**
     * Finds the named identifier
     * @param {string} name
     * @param {?ClassContext} from_class_context
     * @returns {?PHPTypeUnion}
     */
    findStaticIdentifier(name, from_class_context) {
        let m = this.staticIdentifiers[name]
        if(m) {
            if(from_class_context === this || m.scope == "public") {
                return m.types
            }
            // TODO inheritance
        }
        return null
    }
}

/**
 * This defines content that's defined from the global scope, ie. everything
 * that is not anonymous.
 */
class GlobalContext {
    /**
     * Builds the context
     */
    constructor() {
        this.classes = {}
    }
    /**
     * Adds a known class
     * @param {string} name Fully qualified only
     * @returns {ClassContext}
     */
    addClass(name) {
        return this.classes[name] = this.classes[name] || new ClassContext(name)
    }
    /**
     * Finds the class context with the given name
     * @param {string} name Fully qualified only
     * @returns {?ClassContext}
     */
    findClass(name) {
        return this.classes[name]
    }
}

/**
 * This defines the entire context applying to the current node.
 */
export default class Context {
    /**
     * Builds the object
     * @param {?GlobalContext} global_context
     * @param {?ClassContext} class_context
     * @param {FileContext} file_context
     * @param {Object.<string,PHPTypeUnion} ns
     */
    constructor(file_context, global_context, class_context, ns = {}) {
        this.classContext = class_context
        this.globalContext = global_context || new GlobalContext()
        this.fileContext = file_context
        this.isAssigning = false
        this.ns = ns
    }
    /**
     * Adds a name to the namespace list.
     * @param {string} name eg. "$foo"
     * @param {PHPTypeUnion} types
     * @returns {PHPTypeUnion} The original types
     */
    addName(name, types) {
        if(!this.ns[name]) {
            this.ns[name] = new PHPTypeUnion()
        }
        this.ns[name].addTypesFrom(types)
        //console.log(`Types for ${name} are: ${this.ns[name]}`)
        return types
    }
    /**
     * Returns a new context which inherits from this one.
     * @param {boolean} keep_ns
     * @returns {Context}
     */
    childContext(keep_ns = false) {
        return new Context(
            this.fileContext,
            this.globalContext,
            this.classContext,
            keep_ns ? this.ns : {}
        )
    }
    /**
     * If the name is in the namespace, returns its possible types
     * @param {string} name eg "$bar"
     * @returns {PHPTypeUnion}
     */
    findName(name) {
        var types = this.ns[name]
        return types
    }
}
@@ -2930,16 +2930,17 @@
 @param %7B
+?
 Object.%3C
@@ -3037,18 +3037,20 @@
 t, ns = 
-%7B%7D
+null
 ) %7B%0A    
@@ -3247,16 +3247,98 @@
 .ns = ns
+ %7C%7C %7B%0A            '$argv': new PHPTypeUnion(new PHPSimpleType(%22array%22)),%0A        %7D
 %0A    %7D%0A 
@@ -4087,18 +4087,20 @@
 is.ns : 
-%7B%7D
+null
 %0A       

Support a default arg (argv)