import { attempt } from "./stats.js";
import { between, betweenHigh } from "./math.js";
import { weightMap, weightAverage } from "./weightMap.js";
import Shape from "./Shape.js";

const COST_SCORE_RATIO = 1000;

export const paintingBaseSize = 100;

function setText(element, text) {
  element.innerText = element.textContent = text;
}

export default class Painting {
  constructor(shapes) {
    this.shapes = shapes != null ? shapes : [new Shape()];
    this.score = 1 / 0;
    this.maturity = 0;
    this.age = 0;
  }

  paint(canvas, opaque) {
    let ctx, i$, ref$, len$, shape;
    ctx = canvas.getContext("2d");
    if (opaque) {
      ctx.fillStyle = "#ffffff";
      ctx.fillRect(0, 0, Painting.width, Painting.height);
    } else {
      ctx.clearRect(0, 0, Painting.width, Painting.height);
    }
    for (i$ = 0, len$ = (ref$ = this.shapes).length; i$ < len$; ++i$) {
      shape = ref$[i$];
      shape.paint(ctx);
    }
  }

  show(box, targetData) {
    const canvas = box.children[0];
    if (this.canvas !== canvas) {
      this.paint(canvas, true);
      this.diffScore(canvas, targetData);
      this.canvas = canvas;
    }
    if (this.age) {
      const label = `~${this.scoreError.toPrecision(
        5
      )} $${this.scoreCost.toFixed(2)}`;
      return setText(box.children[1], label);
    }
  }

  diffScore(canvas, targetData) {
    const ctx = canvas.getContext("2d");
    let score = 0;
    const data = ctx.getImageData(0, 0, target.width, target.height).data;
    let w = weightMap.length;
    let i = data.length;
    while (i) {
      --i;
      const db = data[--i] - targetData[i];
      const dg = data[--i] - targetData[i];
      const dr = data[--i] - targetData[i];
      score += (dr * dr + dg * dg + db * db) * weightMap[--w];
    }
    this.scoreError = score / (target.width * target.height);
    this.scoreCost =
      (this.cost() * COST_SCORE_RATIO * weightAverage) /
      (target.width * target.height);
    const ctx = canvas.getContext("2d");
    const testData = ctx.getImageData(0, 0, target.width, target.height).data;
    const diffData = ctx.createImageData(target.width, target.height);
    const ddd = diffData.data;
    let i = ddd.length;
    while (i) {
      ddd[--i] = 255;
      ddd[--i] = Math.abs(testData[i] - targetData[i]);
      ddd[--i] = Math.abs(testData[i] - targetData[i]);
      ddd[--i] = Math.abs(testData[i] - targetData[i]);
    }
    return ctx.putImageData(diffData, 0, 0);
  }

  mutate() {
    const child = new Painting(this.shapes.slice(0));
    child.maturity = this.maturity;
    const roll = between(0, 99);
    if (roll < 1 && this.shapes.length > 1) {
      attempt("remove-shape");
      const index = betweenHigh(0, this.shapes.length - 1);
      child.shapes.splice(index, 1);
    } else if (roll < 2) {
      attempt("add-shape");
      child.shapes.push(new Shape());
    } else if (roll < 5 && this.shapes.length >= 2) {
      attempt("reorder-shapes");
      const index = betweenHigh(0, this.shapes.length - 2);
      const tmp = this.shapes[index];
      child.shapes[index] = this.shapes[index + 1];
      child.shapes[index + 1] = tmp;
    } else {
      const index = betweenHigh(0, this.shapes.length - 1);
      child.shapes[index] = this.shapes[index].mutate();
    }
    return child;
  }

  cross(other) {
    const these = this.shapes.slice(0, between(Math.floor(this.shapes.length * .4), Math.ceil(this.shapes.length * .6)));
    const those = other.shapes.slice(between(Math.floor(other.shapes.length * .4), Math.ceil(other.shapes.length * .6)), other.shapes.length);
    attempt("crossover");
    return new Painting([...these, ...those]);
  }

  svg() {
    const w = Painting.width;
    const h = Painting.height;
    return `
      <svg xmlns='http://www.w3.org/2000/svg' 
          width='${w * 10}px' height='${h * 10}px' 
          viewBox='0 0 ${w} ${Painting.height}'>
        <title>Generated by SVG Evo at http://svachalek.github.com/svg_evo</title>
        <defs>
          <clipPath id='clip'>
            <path d='M-1,-1L${w + 1},-1L${w + 1},${h + 1}L-1,${h + 1}Z'/>
          </clipPath>
        </defs>
        <g clip-path='url(#clip)'>
           ${this.shapes.map(shape => shape.svg()).join("")}
        </g>
      </svg>`;
  }

  cost() {
    return this.shapes
      .map(shape => shape.cost())
      .reduce((sum, cost) => sum + cost, 0);
  }
}

Painting.width = paintingBaseSize;
Painting.height = paintingBaseSize;
    return (this.score = this.scoreError + this.scoreCost);
  }

  paintDiffMap(canvas, targetData) {
    this.paint(canvas);