'use strict';
var angular = require('angular');
require('../modules');
require('./GestureScreenLayout');
require('./GestureScreenContext');
var module = angular.module('gesturepassword.services');


module.factory('GraphicsDrawContext', function(GestureScreenLayout,
  GestureScreenContext, GraphicsStateContext) {
  function BackgroundDrawer(screenContext, context) {
    this.screenContext = screenContext;
    this.context = context;
  }

  BackgroundDrawer.prototype.draw = function() {
    var circles = this.screenContext.pointers, circle;
    this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
    this.context.strokeStyle = GraphicsDrawContext.DEFAULT_STROKE_COLOR;
    this.context.beginPath();
    for (var i = 0, len = circles.length; i < len; i++) {
      circle = circles[i];
      this.context.moveTo(circle.x + circle.radius, circle. y);
      this.context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    }
    this.context.stroke();
  };

  function Layer2Drawer(screenContext, context) {
    this.screenContext = screenContext;
    this.context = context;
    this.endPointer = null;
    this.beginPointer = null;
    this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
    this.context.strokeStyle = GraphicsDrawContext.NORMAL_STROKE_COLOR;
    this.context.lineWidth = 3;
  }

  function isSamePointer(p1, p2) {
    return p1 && p2 && p1.x === p2.x && p1.y === p2.y;
  }

  Layer2Drawer.prototype = {
    draw: function() {
      if (this.screenContext.sceneState !== this.sceneState) {
        this.clear();
      }
      this.sceneState = this.screenContext.sceneState;
      if (this.sceneState === GestureScreenContext.STATE_MOVING) {
        var movingPointer = this.screenContext.movingPointer,
          selectors = this.screenContext.selectors,
          beginPointer = selectors.length > 0? this.screenContext.pointers[selectors[selectors.length - 1]] : null,
          samePointer = isSamePointer(beginPointer, this.beginPointer) && 
          isSamePointer(movingPointer, this.endPointer);
        
        if (!samePointer) {
          this.clear();
        }

        this.beginPointer = beginPointer;
        this.endPointer = movingPointer;

        if (beginPointer && movingPointer) {
          this.context.beginPath();
          this.context.moveTo(beginPointer.x, beginPointer.y);
          this.context.lineTo(movingPointer.x, movingPointer.y);
          this.context.stroke();
        }
      }
    },

    clear: function() {
      this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
    }
  };

  function Layer3Drawer(screenContext, context) {
    this.screenContext = screenContext;
    this.context = context;
    this.sceneState = this.screenContext.sceneState;
    this.latestSelectors = null;
    this.mainColor = this.getColor();
    this.innerRadius = GestureScreenLayout.DEFAULT_INNER_RADIUS / (GestureScreenLayout.MIN_RADIUS / this.screenContext.pointers[0].radius);
    this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
  }

  Layer3Drawer.prototype = {
    draw: function() {
      var sceneState = this.screenContext.sceneState,
        addedSelectors = this.screenContext.addedSelectors;
      if (sceneState !== this.sceneState) {
        this.sceneState = sceneState;
        this.latestSelectors = null;
        this.mainColor = this.getColor();
        this.redraw();
      } else if (addedSelectors.length > 0) {
        this.screenContext.addedSelectors = [];
        if (this.latestSelectors !== null) {
          this.drawSelectors([this.latestSelectors].concat(addedSelectors));
        } else {
          this.drawSelectors(addedSelectors);
        }
        this.latestSelectors = addedSelectors[addedSelectors.length - 1];
      }
    },

    redraw: function() {
      this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
      if (this.sceneState !== GestureScreenContext.STATE_PRIMITIVE) {
        this.drawSelectors(this.screenContext.selectors);
      }
    },

    drawSelectors: function(selectors) {
      var circles = this.getCircles(selectors);
      this.context.strokeStyle = this.mainColor;
      this.context.fillStyle = GraphicsDrawContext.BG_COLOR;
      this.drawLines(circles);
      this.drawCircles(circles);
      this.drawInnerCirclesAndDirectionIndicators(circles);
    },

    drawCircles: function(circles) {
      var circle;
      this.context.beginPath();
      for (var i = 0, len = circles.length; i < len; i++) {
        circle = circles[i];
        this.context.moveTo(circle.x + circle.radius, circle.y);
        this.context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
      }
      this.context.stroke();
      this.context.fill();
    },

    drawLines: function(circles) {
      var lines = GraphicsStateContext.generateLines(circles),
        line;
      this.context.save();
      this.context.lineWidth = 3;
      this.context.globalCompositeOperation = 'destination-over';
      this.context.beginPath();
      for (var i = 0, len = lines.length; i < len; i++) {
        line = lines[i];
        this.context.moveTo(line.start.x, line.start.y);
        this.context.lineTo(line.end.x, line.end.y);
      }
      this.context.stroke();
      this.context.restore();
    },

    drawInnerCirclesAndDirectionIndicators: function(circles) {
      this.context.fillStyle = this.mainColor;
      var circle;
      this.context.beginPath();

      for (var i = 0, len = circles.length; i < len; i++) {
        circle = circles[i];
        this.context.moveTo(circle.x + this.innerRadius, circle.y);
        this.context.arc(circle.x, circle.y, this.innerRadius, 0, Math.PI * 2);
      }
      
      this.drawDirectionIndicators(
        GraphicsStateContext.generateDirectionIndicators(circles)
      );
      
      this.context.fill();
    },

    drawDirectionIndicators: function(indicators) {
      var indicator,
        translate = GestureScreenLayout.DEFAULT_INNER_RADIUS + GestureScreenLayout.DEFAULT_INDICATOR_WIDTH / 2;
      for (var i = 0, len = indicators.length; i < len; i++) {
        indicator = indicators[i];
        this.context.save();
        this.context.translate(indicator.x, indicator.y);
        this.context.rotate(indicator.rotate / 180 * Math.PI);
        this.context.translate(translate, 0);
        this.context.moveTo(0, -GestureScreenLayout.DEFAULT_INDICATOR_WIDTH / 2);
        this.context.lineTo(GestureScreenLayout.DEFAULT_INDICATOR_WIDTH, 0);
        this.context.lineTo(0, GestureScreenLayout.DEFAULT_INDICATOR_WIDTH / 2);
        this.context.lineTo(0, -GestureScreenLayout.DEFAULT_INDICATOR_WIDTH / 2);
        this.context.restore();
      }
    },

    getCircles: function(selectors) {
      var circles = [],
        pointers = this.screenContext.pointers;
      for (var i = 0, len = selectors.length; i < len; i++) {
        circles.push(pointers[selectors[i]]);
      }
      return circles;
    },

    getColor: function() {
      var sceneState = this.screenContext.sceneState;
      if (sceneState === GestureScreenContext.STATE_NO) {
        return GraphicsDrawContext.NO_STROKE_COLOR;
      } else if (sceneState === GestureScreenContext.STATE_PRIMITIVE) {
        return GraphicsDrawContext.DEFAULT_STROKE_COLOR;
      } else {
        return GraphicsDrawContext.NORMAL_STROKE_COLOR;
      }
    }
  };

  function GraphicsDrawContext(screenContext, bgContext, layer2Context, layer3Context) {
    this.screenContext = screenContext;
    new BackgroundDrawer(screenContext, bgContext).draw();
    this.layer2Drawer = new Layer2Drawer(screenContext, layer2Context);
    this.layer3Drawer = new Layer3Drawer(screenContext, layer3Context);
  }

  GraphicsDrawContext.BG_COLOR = 'rgb(35, 39, 54)';
  GraphicsDrawContext.DEFAULT_STROKE_COLOR = 'rgb(149, 155, 180)';
  GraphicsDrawContext.NORMAL_STROKE_COLOR = 'rgb(0, 170, 255)';
  GraphicsDrawContext.NO_STROKE_COLOR = 'rgb(255, 51, 0)';

  GraphicsDrawContext.prototype = {
    draw: function() {
      this.layer2Drawer.draw();
      this.layer3Drawer.draw();
      if (window.meter) {
        meter.tick();
      }
      return;
    }
  };

  return GraphicsDrawContext;
});