namespace pxsim.visuals {
    export function normalizeId(prefix: string, svgId: string) {
        return `${prefix}-${svgId}`;
    }

    export function normalizeXml(prefix: string, xml: string): string {
        xml = xml.replace(/id=\"(.*?)\"/g, (m: string, id: string) => {
            return `id="${normalizeId(prefix, id)}"`;
        });
        xml = xml.replace(/url\(#(.*?)\)/g, (m: string, id: string) => {
            return `url(#${normalizeId(prefix, id)})`;
        });
        xml = xml.replace(/xlink:href=\"#(.*?)\"/g, (m: string, id: string) => {
            return `xlink:href="#${normalizeId(prefix, id)}"`;
        });
        return xml;
    }

    export class ModuleView extends View implements LayoutElement {
        protected content: SVGSVGElement;

        protected controlShown: boolean;

        protected opacity: number;

        constructor(protected xml: string, protected prefix: string, protected id: NodeType, protected port: NodeType) {
            super();
            this.xml = normalizeXml(this.prefix, xml);
        }

        private normalizeXml(xml: string) {
            return pxsim.visuals.normalizeXml(this.prefix, xml);
        }

        protected normalizeId(svgId: string) {
            return `${this.prefix}-${svgId}`;
        }

        public getId() {
            return this.id;
        }

        public getPort() {
            return this.port;
        }

        public getPaddingRatio() {
            return 0;
        }

        public getWiringRatio() {
            return 0.5;
        }

        protected buildDom(): SVGElement {
            this.content = svg.parseString(this.xml);
            this.buildDomCore();
            this.attachEvents();
            if (this.hasClick())
                this.content.style.cursor = "pointer";
            return this.content;
        }

        protected buildDomCore() {

        }

        public getInnerHeight() {
            if (!this.content) {
                return 0;
            }
            if (!this.content.hasAttribute("viewBox")) {
                return parseFloat(this.content.getAttribute("height"));
            }
            return parseFloat(this.content.getAttribute("viewBox").split(" ")[3]);
        }

        public getInnerWidth() {
            if (!this.content) {
                return 0;
            }
            if (!this.content.hasAttribute("viewBox")) {
                return parseFloat(this.content.getAttribute("width"));
            }
            return parseFloat(this.content.getAttribute("viewBox").split(" ")[2]);
        }

        public attachEvents() {
        }

        public resize(width: number, height: number) {
            super.resize(width, height);
            this.updateDimensions(width, height);
        }

        private updateDimensions(width: number, height: number) {
            if (this.content) {
                const currentWidth = this.getInnerWidth();
                const currentHeight = this.getInnerHeight();
                const newHeight = currentHeight / currentWidth * width;
                const newWidth = currentWidth / currentHeight * height;
                this.content.setAttribute('width', `${width}`);
                this.content.setAttribute('height', `${newHeight}`);
            }
        }

        public hasClick() {
            return true;
        }

        public setSelected(selected: boolean) {
            super.setSelected(selected);
            this.updateOpacity();
        }

        public updateState() {
            this.updateOpacity();
        }

        protected updateOpacity() {
            if (this.rendered) {
                const opacity = this.selected ? 0.2 : 1;
                if (this.hasClick() && this.opacity != opacity) {
                    this.opacity = opacity;
                    this.setOpacity(this.opacity);
                    if (this.selected) this.content.style.cursor = "";
                    else this.content.style.cursor = "pointer";
                }
            }
        }

        protected setOpacity(opacity: number) {
            this.element.setAttribute("opacity", `${opacity}`);
        }
    }
}