/// <reference path="leaf.ts" />
namespace Leaf {
    export class ErrorDoc<TErrors> {
        private errorNames: string[]
        private errors: TErrors = {} as TErrors
        static create<TErrors>(): ErrorDoc<TErrors> {
            return new ErrorDoc<TErrors>()
        }
        public define(name: string, base?: string | Error | Function, defaultProperty?: any) {
            let Base: Function = Error
            if (typeof base === "string") {
                let _ = this[base]
                if (typeof _ === "function") {
                    Base = _
                }
            } else if (typeof base === "function") {
                Base = base
            } else if (base) {
                console.warn("Unknown base error", base)
            }
            let E = createError(name, Base, defaultProperty)
            this.errors[name] = E
            return this
        }
        public generate() {
            return this.errors
        }
    }
    export function createError(name: string, base?: Error | Function, defaultProperty?: any) {
        let E = function (message: string, detail: any) {
            if (typeof message === "object") {
                if (!detail) {
                    detail = message
                    message = name
                } else {
                    try {
                        message = JSON.stringify(message, null, 4)
                    } catch (e) {
                        message = name + "Warning: recursive error property" + e.toString()
                    }
                }
            }
            this.name = name
            this.message = message
            if (typeof defaultProperty === "function") {
                let _ = base
                base = defaultProperty
                defaultProperty = _
            }
            if (!detail) detail = {}
            if (!defaultProperty) defaultProperty = {}
            for (let prop in defaultProperty) {
                this[prop] = defaultProperty[prop]
            }
            for (let prop in detail) {
                this[prop] = detail[prop]
            }
        }
        interface CTR {
            (message: string, detail: any): void
        }
        let Base = base as CTR
        E.prototype = new Base(name, {})
        E.prototype.toJSON = function () {
            let result = {}
            for (let prop in this) {
                if (this.hasOwnProperty(prop) && typeof this[prop] !== "function") {
                    result[prop] = this[prop]
                }
            }
            if (this.message) {
                result["message"] = this.message
            }
            return result
        }
        return E
    }
    export type Partial<T> = {
        [P in keyof T]?: T[P]
    }
    export interface CustomErrorConstructor<TDetail> {
        //new (message?: string, detail?: any): void
        new (message?: string, detail?: Partial<TDetail>): TDetail & Error
    }

    export const Errors = ErrorDoc.create()
        .define("Timeout")
        .define("Abort")
        .define("IOError")
        .define("NetworkError")
        .generate() as ErrorsOf<{
            Timeout: {}
            Abort: {}
            IOError: {}
            NetworkError: {}
        }>
    export type ErrorsOf<T> = {
        [P in keyof T]?: CustomErrorConstructor<T[P]>;
    }
}
