import imageSource = require("image-source");
import colorModule = require("color");
import enums = require("ui/enums");
import definition = require("ui/styling/background");
import cssValue = require("css-value");
import utils = require("utils/utils");
import { isAndroid }  from "platform"; 

import * as typesModule from "utils/types";
var types: typeof typesModule;
function ensureTypes() {
    if (!types) {
        types = require("utils/types");
    }
}

interface CSSValue {
    type: string;
    string: string;
    unit?: string;
    value?: number;
}

export class Background implements definition.Background {
    public static default = new Background(undefined, undefined, undefined, undefined, undefined, 0, undefined, 0, undefined);

    color: colorModule.Color;
    image: imageSource.ImageSource;
    repeat: string;
    position: string;
    size: string;
    // The ones below are used on Android only
    borderWidth: number = 0;
    borderColor: colorModule.Color;
    borderRadius: number = 0;
    clipPath: string;

    constructor(
        color: colorModule.Color,
        image: imageSource.ImageSource,
        repeat: string,
        position: string,
        size: string,
        borderWidth: number,
        borderColor: colorModule.Color,
        borderRadius: number,
        clipPath: string
    ) {
        this.color = color;
        this.image = image;
        this.repeat = repeat;
        this.position = position;
        this.size = size;
        this.borderWidth = borderWidth;
        this.borderColor = borderColor;
        this.borderRadius = borderRadius;
        this.clipPath = clipPath;
    }

    public withColor(value: colorModule.Color): Background {
        return new Background(value, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath);
    }

    public withImage(value: imageSource.ImageSource): Background {
        return new Background(this.color, value, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath);
    }

    public withRepeat(value: string): Background {
        return new Background(this.color, this.image, value, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath);
    }

    public withPosition(value: string): Background {
        return new Background(this.color, this.image, this.repeat, value, this.size, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath);
    }

    public withSize(value: string): Background {
        return new Background(this.color, this.image, this.repeat, this.position, value, this.borderWidth, this.borderColor, this.borderRadius, this.clipPath);
    }
    
    public withBorderWidth(value: number): Background {
        return new Background(this.color, this.image, this.repeat, this.position, this.size, value, this.borderColor, this.borderRadius, this.clipPath);
    }

    public withBorderColor(value: colorModule.Color): Background {
        return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, value, this.borderRadius, this.clipPath);
    }

    public withBorderRadius(value: number): Background {
        return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, value, this.clipPath);
    }

    public withClipPath(value: string): Background {
        return new Background(this.color, this.image, this.repeat, this.position, this.size, this.borderWidth, this.borderColor, this.borderRadius, value);
    }

    public getDrawParams(width: number, height: number): definition.BackgroundDrawParams {
        if (!this.image) {
            return null;
        }

        var res: definition.BackgroundDrawParams = {
            repeatX: true,
            repeatY: true,
            posX: 0,
            posY: 0,
        }

        // repeat
        if (this.repeat) {
            switch (this.repeat.toLowerCase()) {
                case enums.BackgroundRepeat.noRepeat:
                    res.repeatX = false;
                    res.repeatY = false;
                    break;

                case enums.BackgroundRepeat.repeatX:
                    res.repeatY = false;
                    break;

                case enums.BackgroundRepeat.repeatY:
                    res.repeatX = false;
                    break;
            }
        }

        var imageWidth = this.image.width;
        var imageHeight = this.image.height;

        // size
        if (this.size) {
            let values = cssValue(this.size);

            if (values.length === 2) {
                let vx = values[0];
                let vy = values[1];
                if (vx.unit === "%" && vy.unit === "%") {
                    imageWidth = width * vx.value / 100;
                    imageHeight = height * vy.value / 100;

                    res.sizeX = imageWidth;
                    res.sizeY = imageHeight;
                }
                else if (vx.type === "number" && vy.type === "number" &&
                    ((vx.unit === "px" && vy.unit === "px") || (vx.unit === "" && vy.unit === ""))) {
                    imageWidth = vx.value;
                    imageHeight = vy.value;

                    res.sizeX = imageWidth;
                    res.sizeY = imageHeight;
                }
            }
            else if (values.length === 1 && values[0].type === "ident") {
                let scale = 0;

                if (values[0].string === "cover") {
                    scale = Math.max(width / imageWidth, height / imageHeight);
                }
                else if (values[0].string === "contain") {
                    scale = Math.min(width / imageWidth, height / imageHeight);
                }

                if (scale > 0) {
                    imageWidth *= scale;
                    imageHeight *= scale;

                    res.sizeX = imageWidth;
                    res.sizeY = imageHeight;
                }
            }
        }

        // position
        if (this.position) {
            let v = Background.parsePosition(this.position);
            if (v) {
                let spaceX = width - imageWidth;
                let spaceY = height - imageHeight;

                if (v.x.unit === "%" && v.y.unit === "%") {
                    res.posX = spaceX * v.x.value / 100;
                    res.posY = spaceY * v.y.value / 100;
                }
                else if (v.x.type === "number" && v.y.type === "number" &&
                    ((v.x.unit === "px" && v.y.unit === "px") || (v.x.unit === "" && v.y.unit === ""))) {
                    res.posX = v.x.value;
                    res.posY = v.y.value;
                }
                else if (v.x.type === "ident" && v.y.type === "ident") {
                    if (v.x.string.toLowerCase() === "center") {
                        res.posX = spaceX / 2;
                    }
                    else if (v.x.string.toLowerCase() === "right") {
                        res.posX = spaceX;
                    }

                    if (v.y.string.toLowerCase() === "center") {
                        res.posY = spaceY / 2;
                    }
                    else if (v.y.string.toLowerCase() === "bottom") {
                        res.posY = spaceY;
                    }
                }
            }
        }

        return res;
    }

    private static parsePosition(pos: string): { x: CSSValue, y: CSSValue } {
        let values = cssValue(pos);

        if (values.length === 2) {
            return {
                x: values[0],
                y: values[1]
            };
        }

        if (values.length === 1 && values[0].type === "ident") {
            let val = values[0].string.toLocaleLowerCase();
            let center = {
                type: "ident",
                string: "center"
            };
            
            // If you only one keyword is specified, the other value is "center"
            if (val === "left" || val === "right") {
                return {
                    x: values[0],
                    y: center
                };
            }

            else if (val === "top" || val === "bottom") {
                return {
                    x: center,
                    y: values[0]
                };
            }

            else if (val === "center") {
                return {
                    x: center,
                    y: center
                };
            }
        }

        return null;
    };

    public isEmpty(): boolean {
        ensureTypes();

        if (isAndroid){
            return types.isNullOrUndefined(this.image) 
                && types.isNullOrUndefined(this.color)
                && !this.borderWidth
                && !this.borderRadius
                && !this.clipPath;
        }
        else {
            return types.isNullOrUndefined(this.image) 
                && types.isNullOrUndefined(this.color);    
        }
    }

    public static equals(value1: Background, value2: Background): boolean {
        // both values are falsy
        if (!value1 && !value2) {
            return true;
        }

        // only one is falsy
        if (!value1 || !value2) {
            return false;
        }

        if (isAndroid){
            return value1.image === value2.image 
                && value1.position === value2.position 
                && value1.repeat === value2.repeat 
                && value1.size === value2.size 
                && colorModule.Color.equals(value1.color, value2.color)
                && value1.borderWidth === value2.borderWidth
                && colorModule.Color.equals(value1.borderColor, value2.borderColor) 
                && value1.borderRadius === value2.borderRadius
                && value1.clipPath === value2.clipPath;
        }
        else {
            return value1.image === value2.image 
                && value1.position === value2.position 
                && value1.repeat === value2.repeat 
                && value1.size === value2.size 
                && colorModule.Color.equals(value1.color, value2.color);
        }
        
    }
    
    public toString(): string {
        return `isEmpty: ${this.isEmpty()}; color: ${this.color}; image: ${this.image}; repeat: ${this.repeat}; position: ${this.position}; size: ${this.size}; borderWidth: ${this.borderWidth}; borderColor: ${this.borderColor}; borderRadius: ${this.borderRadius}; clipPath: ${this.clipPath};`;
    }
}

export function cssValueToDevicePixels(source: string, total: number): number {
    var result;
    source = source.trim();

    if (source.indexOf("px") !== -1) {
        result = parseFloat(source.replace("px", ""));
    }
    else if (source.indexOf("%") !== -1 && total > 0) {
        result = (parseFloat(source.replace("%", "")) / 100) * utils.layout.toDeviceIndependentPixels(total);
    } else {
        result = parseFloat(source);
    }
    return utils.layout.toDevicePixels(result);
}