/// <reference path="../../Bodypart.ts" />
/// <reference path="../../Humanoid/Humanoid.ts" />
/// <reference path="../SexStick.ts" />
/// <reference path="../SexHole.ts" />
/// <reference path="../../../Measure.ts" />
/// <reference path="../../../../../Elements/Classes/Say/OneOf.ts" />
class HumanoidPenis extends SexStick {

    public sorenessWeight = Bodypart.WEIGHT_HIGH;
    public genderValue = 25;
    public genderWeight = Bodypart.WEIGHT_HIGHEST;
    public slots : Array<number> = [Humanoid.SLOT_CROTCH_FRONT];
    public sluttiness = Bodypart.SLUTTINESS_HIGH_SLUT;
    public sluttinessWeight = Bodypart.WEIGHT_MEDIUM;

    public flaccidSize = new Measure(6);
    public flaccidWidth = new Measure(3);
    public erectSize = new Measure(15);
    public erectWidth = new Measure(6);

    private arousalMinimum : number = 5;
    private arousalErect : number = 25;
    private flaccidMult : number;

    public getSluttiness () {
        return this.getBulgeSize() * 3;
    }

    public isGrower () {
        return this.flaccidMult < 0.5;
    }

    public constructor (options? : ThingOptions) {
        super(options);
        if (Math.random() >= 0.5) {
            // grower?
            this.flaccidMult = 0.33;
        } else {
            // shower?
            this.flaccidMult = 0.77;
        }
        this.addGetAlterations((thing) => {
            return {
                FSize : this.flaccidSize.getNumber(),
                FWidth : this.flaccidWidth.getNumber(),
                ESize : this.erectSize.getNumber(),
                EWidth : this.erectWidth.getNumber(),
                AMin : this.arousalMinimum,
                AErect : this.arousalErect,
                FMult : this.flaccidMult
            }
        });

        this.addSetAlterations((thing, changes) => {
            this.flaccidSize = new Measure(changes.FSize);
            this.flaccidWidth = new Measure(changes.FWidth);
            this.erectSize = new Measure(changes.ESize);
            this.erectWidth = new Measure(changes.EWidth);
            this.arousalMinimum = changes.AMin;
            this.arousalErect = changes.AErect;
            this.flaccidMult = changes.FMult;
        });
    }

    public getActualSize () {
        let min = this.flaccidSize.getNumber();
        let max = this.erectSize.getNumber();
        let variableSize = (max - min);

        let finalSize = min + (variableSize * this.getArousalPerc());
        return finalSize;
    }

    public isBig () {
        return this.getBulgeSize() > 22;
    }

    public getArousalPerc () {
        //TODO: Get arousal from owner
        let arousal = 0;
        let numSteps = this.arousalErect - this.arousalMinimum;
        let arousalPerc = (arousal - this.arousalMinimum) / numSteps;
        arousalPerc = arousalPerc < 0 ? 0 :
            arousalPerc > 1 ? 1 :
                arousalPerc;
        return arousalPerc;
    }

    public isFlaccid () {
        return this.getArousalPerc() < 0.6;
    }

    public isErect () {
        return !this.isFlaccid();
    }

    public getActualWidth () {
        let min = this.flaccidWidth.getNumber();
        let max = this.erectWidth.getNumber();
        let variableSize = (max - min);
        let numSteps = this.arousalErect - this.arousalMinimum;

        //TODO: Get arousal from owner
        let arousal = 0;
        let arousalPerc = (arousal - this.arousalMinimum) / numSteps;
        arousalPerc = arousalPerc < 0 ? 0 :
            arousalPerc > 1 ? 1 :
                arousalPerc;

        let finalSize = min + (variableSize * arousalPerc);
        return finalSize;
    }

    public getLong () {
        return new Measure(this.getActualSize());
    }

    public getWide () {
        return new Measure(this.getActualWidth());
    }

    public getBulgeSize () {
        return this.getActualSize() + (3 * this.getActualWidth());
    }

    public getSizeText () {
        return HumanoidPenis.getSizeText(this.getBulgeSize());
    }

    public static getSizeText (size : number) {
        // size = long + (width * 3)
        // so long from 1 to 20, lets say
        // width from 1 to 6, so 3 to 18
        let sizeTable = [
            [0, "nonexistent"],
            [1, "tiny"], // 3l 2w = 9
            [20, "small"], // 10l 3w = 19
            [23, "medium"], // 14l 3w = 24
            [27, "big"],
            [32, "huge"],
            [36, "monstrous"]
        ];

        let i;
        for (i = 1; i < sizeTable.length && sizeTable[i][0] < size; i++) {}
        return sizeTable[i - 1][1];
    }

    public getGenderValue () {
        let sizeTable = [
            [10, 60], // 3l 2w = 9
            [20, 45], // 10l 3w = 19
            [23, 35], // 14l 3w = 24
            [27, 28],
            [32, 24],
            [36, 18]
        ];

        let i;
        for (i = 1; i < sizeTable.length && sizeTable[i][0] < this.getActualSize(); i++) {}
        return sizeTable[i - 1][1];
    }

    public arrangeGenderValue (gv : number) {
        let sizeTable = [
            [60, 3, 2], // 3l 2w = 9
            [45, 10, 3], // 10l 3w = 19
            [35, 14, 3], // 14l 3w = 24
            [28, 17, 3],
            [24, 19, 4],
            [18, 22, 4.5]
        ];

        let i;
        for (i = 1; i < sizeTable.length && sizeTable[i][0] > gv; i++) {}

        this.flaccidSize = new Measure(this.flaccidMult * sizeTable[i - 1][1]);
        this.flaccidWidth = new Measure(this.flaccidMult * sizeTable[i - 1][2]);
        this.erectSize = new Measure(sizeTable[i - 1][1]);
        this.erectWidth = new Measure(sizeTable[i - 1][2]);

        (<Humanoid> this.getPartOne()).invalidateCaches();
    }

    public static getSynonym () {
        // TODO: Add more when creative.
        let cockNames = [
            "cock",
            "dick"
        ];
        return (new OneOf(OneOf.PURELY_AT_RANDOM, ...cockNames).getOne());
    }
}