﻿function BonMapza(map, config) {
  if (!(map instanceof GMap2)) return;

  // private properties

  var $this  = this,
      emptyFn = function(){},
      lines = {},
      poly = {},
      idSeed = 99,
      debug,
      Proj;

  // public properties

  $this.map = map;
  $this.version = "1.2";


  // private methods

  var clone = function(obj) {
    if(obj == null || typeof(obj) != 'object') return obj;
    var temp = new obj.constructor(); // changed (twice)
    for(var key in obj) temp[key] = clone(obj[key]);
    return temp;
  }

  var genId = function() {
    return "l" + (++idSeed)
  }

  var isArray = function(v) {
    return Object.prototype.toString.apply(v) === '[object Array]';
  }

  var isObject = function(v) {
    return v && typeof v == "object";
  }
  var isEmpty = function(v){
      return v === null || v === undefined || ((isArray(v) && !v.length));
  }

  var applyIf = function(o, c) {
    if(o){
      for(var p in c){
        if(isEmpty(o[p])){
          o[p] = c[p];
        }
      }
    }
    return o;
  }

  var apply = function(o, c, defaults){
    if(defaults) apply(o, defaults);

    if(o && c && typeof c == 'object'){
      for(var p in c){
        o[p] = c[p];
      }
    }
    return o;
  };


  // config !!

    var _c = applyIf(config, {
      click: emptyFn,
      ob: emptyFn
  });

  // config !!

  var markerDrag = function(point) {
    var i, poly, new_poly;
    for(i in lines[this.line_index].polylines) {
      poly = lines[this.line_index].polylines[i];
      if (poly.point_index == this.point_index-1) {
        new_poly = new_polyline([poly.getVertex(0), point], lines[this.line_index].points[poly.point_index], poly.point_index, poly.line_index);
      } else if (poly.point_index == this.point_index) {
        new_poly = new_polyline([point, poly.getVertex(1)], lines[this.line_index].points[poly.point_index], poly.point_index, poly.line_index);
      } else continue;
      $this.map.removeOverlay(poly);
      $this.map.addOverlay(new_poly);
      lines[poly.line_index].polylines[i] = new_poly;
    }
  }

  var tmp_marker;

  var drag_flag = false;


  var tmp_markerDrag = function(point) {
    $this.map.removeOverlay(this.poly1);
    $this.map.removeOverlay(this.poly2);

    this.poly1= new_polyline([this.poly.getVertex(0), point], lines[this.line_index].points[this.poly.point_index], this.poly.point_index, this.poly.line_index);
    this.poly2= new_polyline([point, this.poly.getVertex(1)], lines[this.line_index].points[this.poly.point_index], this.poly.point_index+1, this.poly.line_index);

    $this.map.addOverlay(this.poly1);
    $this.map.addOverlay(this.poly2);

  }

  var tmp_markerDragStart = function(point) {

    drag_flag = true;

    for(var i in lines[this.line_index].polylines) {
      if (this.point_index == lines[this.line_index].polylines[i].point_index) {
        var poly = lines[this.line_index].polylines[i];
        break;
      }
    }

    var poly1= new_polyline([poly.getVertex(0), point], lines[this.line_index].points[poly.point_index], poly.point_index, poly.line_index);
    var poly2= new_polyline([point, poly.getVertex(1)], lines[this.line_index].points[poly.point_index], poly.point_index+1, poly.line_index);

    $this.map.removeOverlay(poly);
    $this.map.addOverlay(poly1);
    $this.map.addOverlay(poly2);

    this.poly1 = poly1;
    this.poly2 = poly2;
    this.poly = poly;
  }

  var tmp_markerDragEnd = function(point) {
    drag_flag = false;




    var tmp_point = clone(lines[this.line_index].points[this.poly.point_index]);
    var tmp_array = lines[this.line_index].points.splice(this.poly.point_index, lines[this.line_index].points.length);
    tmp_point.lat = point.lat();
    tmp_point.lng = point.lng();
    tmp_array.unshift(tmp_point);
    lines[this.line_index].points = lines[this.line_index].points.concat(tmp_array);


    var poly_index;

    for(var i in lines[this.line_index].polylines) {
      if (this.point_index == lines[this.line_index].polylines[i].point_index) {
        poly_index = i;
        break;
      }
    }



   // console.log(lines[this.line_index]);
    tmp_array = lines[this.line_index].polylines.splice(Number(poly_index)+1, lines[this.line_index].polylines.length);
    lines[this.line_index].polylines.pop();
    lines[this.line_index].polylines.push(this.poly1, this.poly2);
    for(var i in tmp_array) {
      tmp_array[i].point_index += 1;
    }
    lines[this.line_index].polylines = lines[this.line_index].polylines.concat(tmp_array);

    for(var i in lines[this.line_index].markers) {
      if (lines[this.line_index].markers[i].point_index > this.point_index) {
        lines[this.line_index].markers[i].point_index += 1;
      }
    }

    var iconNode = new GIcon(); iconNode.image = '/img/m.png';
    iconNode.shadow = ''; iconNode.iconSize = new GSize(10,10); iconNode.shadowSize = new GSize(0,0);
    iconNode.iconAnchor = new GPoint(5,5); iconNode.infoWindowAnchor = new GPoint(5,5);
    iconNode.dragCrossImage = '/img/empty.gif';
    iconNode.dragCrossSize = GSize(1, 1);
    iconNode.maxHeight = 1;

    var marker = new GMarker(point, {icon:iconNode, draggable:true, bouncy:false, zIndexProcess:function(marker,b) {return 1;}});
    marker.point_index = this.point_index+1;
    marker.line_index = this.line_index;
    GEvent.addListener(marker, "drag", markerDrag);
    $this.map.addOverlay(marker);

    //console.log(tmp_array);
    //console.log(lines[this.line_index]);


  }

  var paint_flag = false;
  var paint_id = false;
  var pp = {};
  var paint_poly;

  var mapMouseMove = function(a,b,c) {

    if (drag_flag) return;

    var d, i, p, p1x, p1y, p2x, p2y, x, y, nx, ny, dist, flag_x;

    x = Proj.fromLatLngToPixel(a, $this.map.getZoom()).x;
    y = Proj.fromLatLngToPixel(a, $this.map.getZoom()).y;

    //paint
    if (paint_flag) {
      if (!paint_id) {
        paint_id = genId();
        pp[paint_id] = [];
      }
      var len = pp[paint_id].length;

      var paint = function() {
        pp[paint_id].push(a);
        paint_poly.insertVertex(len, a);
      }

      if (len == 0) {
        paint_poly = new GPolyline([], 'green',3,1);
        $this.map.addOverlay(paint_poly);
        paint();
      } else {
        dpx = x - Proj.fromLatLngToPixel(pp[paint_id][len - 1], $this.map.getZoom()).x;
        dpy = y - Proj.fromLatLngToPixel(pp[paint_id][len - 1], $this.map.getZoom()).y;
        d = Math.sqrt(dpx*dpx + dpy*dpy);
        if (d > 10) paint();
      }
    }


    // show drag marker


    if (!tmp_marker) {
      iconNode = new GIcon(); iconNode.image = '/img/node.gif';
      iconNode.shadow = ''; iconNode.iconSize = new GSize(10,10); iconNode.shadowSize = new GSize(0,0);
      iconNode.iconAnchor = new GPoint(5,5); iconNode.infoWindowAnchor = new GPoint(5,5);
      iconNode.dragCrossImage = '/img/empty.gif';
      iconNode.dragCrossSize = GSize(1, 1);
      iconNode.maxHeight = 1;

      tmp_marker = new GMarker($this.map.getCenter(), {icon:iconNode, draggable:true, bouncy:false, zIndexProcess:function(marker,b) {return 1;}});
      GEvent.addListener(tmp_marker, "drag", tmp_markerDrag);
      GEvent.addListener(tmp_marker, "dragstart", tmp_markerDragStart);
      GEvent.addListener(tmp_marker, "dragend", tmp_markerDragEnd);
      tmp_marker.OnMap = false;
    }
    tmp_marker.showOnMap = false;



    for (i in lines) {
      for (p in lines[i].polylines) {
        p1x = Proj.fromLatLngToPixel(lines[i].polylines[p].getVertex(0), $this.map.getZoom()).x;
        p1y = Proj.fromLatLngToPixel(lines[i].polylines[p].getVertex(0), $this.map.getZoom()).y;
        p2x = Proj.fromLatLngToPixel(lines[i].polylines[p].getVertex(1), $this.map.getZoom()).x;
        p2y = Proj.fromLatLngToPixel(lines[i].polylines[p].getVertex(1), $this.map.getZoom()).y;

        dpx = p2x - p1x;
        dpy = p2y - p1y;
        d = dpx*dpx + dpy*dpy;

        u = ((x - p2x) * dpx + (y- p2y)* dpy)/d;

        nx = p2x + (u*dpx);
        ny = p2y + (u*dpy);

        dist = (x - nx)*(x - nx) + (y - ny)*(y - ny);

        if ((Math.abs(p2x-nx)+Math.abs(p1x-nx) == Math.abs(p2x-p1x) || Math.abs(p2y-ny)+Math.abs(p1y-ny) == Math.abs(p2y-p1y)) && dist < 100) {
          tmp_marker.showOnMap = true;
          tmp_marker.setLatLng(Proj.fromPixelToLatLng(new GPoint(nx,ny), $this.map.getZoom()));
          tmp_marker.point_index = lines[i].polylines[p].point_index;
          tmp_marker.line_index = lines[i].polylines[p].line_index;
        }
      }
    }

    if (tmp_marker.showOnMap && !tmp_marker.OnMap) {
      tmp_marker.OnMap = true;
      $this.map.addOverlay(tmp_marker);
    } else if (!tmp_marker.showOnMap && tmp_marker.OnMap) {
      tmp_marker.OnMap = false;
      $this.map.removeOverlay(tmp_marker);
    }


    return;
    if (!tmp_marker) {
        tmp_marker = new GMarker(a);
      };
    if (polyflag) {
      tmp_marker.setLatLng(a);
      if (!tmp_marker.onMap) {
        tmp_marker.onMap = true;
        $this.map.addOverlay(tmp_marker);
      }
    } else {
      $this.map.removeOverlay(tmp_marker);
      tmp_marker.onMap = false;
    }
  }

  var new_polyline = function(latlng_array, config, point_index, line_index) {
    var polyOptions = {

    };

    var poly = new GPolyline(latlng_array, config.line_color, config.line_size, config.line_opacity, polyOptions)
    ;
    poly.point_index = point_index;
    poly.line_index = line_index;

    return poly;
  }

  var test = function(){
    if (paint_flag) {
      _c.ob(pp[paint_id]);
      paint_id = false;

    }
    paint_flag = !paint_flag;

  };

  // publuic methods

  this.createLine = function(points, config) {
    if (!isArray(points) || !isObject(config)) return;
    var i;
    var line = {
      markers: [],
      polylines: []
    }
    var _c = applyIf(config, {
      line_size: null,
      line_color: null,
      line_opacity: null
    });

    var default_point = {
      marker: false,
      draggable: false,
      click: emptyFn,
      line_size: _c.line_size,
      line_color: _c.line_color,
      line_opacity: _c.line_opacity
    }
    var _p = []; //local points var
    for(i in points){
      if (!isObject(points[i])) continue;
      _p.push(applyIf(points[i], default_point));
    }
    var markerOptions, marker, id = genId();
    for(i=0,len=_p.length;i<len;i++) {
      if (i != 0) line.polylines.push(new_polyline([new GLatLng(_p[i-1].lat, _p[i-1].lng), new GLatLng(_p[i].lat, _p[i].lng)], _p[i-1], i-1, id));

      if (_p[i].marker) {
        markerOptions= {
          draggable: _p[i].draggable
        }
        marker = new GMarker(new GLatLng(_p[i].lat, _p[i].lng), markerOptions);
        marker.point_index = i;
        marker.line_index = id;
        GEvent.addListener(marker, "drag", markerDrag);
        //GEvent.addListener(marker, "dragstart", markerDragStart);
        GEvent.addListener(marker,'mousedown',_p[i].click);
        line.markers.push(marker);
      }
    }

    line.points = _p;
    for(i in line.polylines) $this.map.addOverlay(line.polylines[i]);
    for(i in line.markers) $this.map.addOverlay(line.markers[i]);

    lines[id] = line;
    return id;
  }

  var ctrl = false;

  this.createPolygon = function(points, config) {
    if (!isArray(points) || !isObject(config)) return;
    var i;
    var _p = []; //local points var
    for(i in points){
      if (!isObject(points[i])) continue;
      _p.push(new GLatLng(points[i].lat, points[i].lng));
    }
    var _c = applyIf(config, {
      line_size: null,
      line_color: null,
      line_opacity: null,
      fill_color: null,
      fill_opacity: null,
      click: emptyFn
    });

    var newPoly = new GPolygon(_p, _c.line_color, _c.line_size, _c.line_opacity, _c.fill_color, _c.fill_opacity);
    var id = genId();
    newPoly.poly_config = _c;
    newPoly.poly_index = id;
    newPoly.clickFn = function(latlng){
     if (ctrl) return;
     newPoly.poly_config.click(latlng);
    };
    GEvent.addListener(newPoly,'click', newPoly.clickFn);

    poly[id] = newPoly;

    $this.map.addOverlay(poly[id]);

    return id;

  }

  this.marker = function(point, config) {

  }

  this.getLinePoints = function(id) {
    return clone(lines[id].points);
  }

  // constructor

  var md = new MDraggablePolys();

  $this.map.addControl(md);
  GEvent.addListener($this.map, "mousemove", mapMouseMove);
  GEvent.addListener($this.map, "singlerightclick", test);
  Proj = G_NORMAL_MAP.getProjection();

  document.onkeydown = function(a) {
    var key = (a && a.keyCode) || window.event.keyCode;
    if (key == 17) ctrl = true;
  }

  document.onkeyup = function(a) {
    var key = (a && a.keyCode) || window.event.keyCode;
    if (key == 17) ctrl = false;
  }

  GEvent.addListener($this.map,'click',function(overlay, latlng, overlaylatlng){
    if (overlay && ctrl) {
      if (overlay instanceof GPolygon) {
        $this.map.closeInfoWindow();
        md.draggingOn(overlay);
      }
    }
    _c.click(overlay, latlng, overlaylatlng);

  })
};

function MDraggablePolys(MOptions) {
  this.position = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(0,0));
}

var bm, line, i=0;

function init() {
  window.onunload = GUnload;

  MDraggablePolys.prototype = new GControl(false,false);


  MDraggablePolys.prototype.initialize = function(map) {
    this.map = map;
    this.self = this;
    this.center = this.map.getCenter();
    this.zoom = this.map.getZoom();

    this.listeners = Array();
    this.container = document.createElement('div');
    this.container.style.display = 'none';
    this.innerDiv = this.createInnerDiv();
    this.container.appendChild(this.innerDiv);
    this.map.getContainer().appendChild(this.container);
    return this.container;
  };

  MDraggablePolys.prototype.createInnerDiv = function() {
    var self = this.self;

    var oDiv = document.createElement('div');
    var mapW = this.map.getContainer().clientWidth;
    var mapH = this.map.getContainer().clientHeight;

    this.insetMap = new GMap2(oDiv,{size: new GSize(mapW,mapH)});
    this.insetMap.setCenter(this.center,this.zoom);

    var CopyrightDiv = oDiv.firstChild.nextSibling;
    var CopyrightImg = oDiv.firstChild.nextSibling.nextSibling;
    CopyrightDiv.style.display = "none";
    CopyrightImg.style.display = "none";

    this.blankMap = this.createBlankMap();
    this.insetMap.addMapType(this.blankMap);
    this.insetMap.setCenter(this.center,this.zoom,this.blankMap);

    oDiv.style.background = '';
    oDiv.style.border = '3px dashed blue';


    var listener = GEvent.addListener(this.map,'zoomend',function(oldZ,newZ){
      self.insetMap.setCenter(self.map.getCenter(),self.map.getZoom());
    })
    this.listeners.push(listener);

    var listener = GEvent.addListener(this.map,'moveend',function(){
      self.insetMap.setCenter(self.map.getCenter(),self.map.getZoom());
    })
    this.listeners.push(listener);

    var listener = GEvent.addListener(this.insetMap,'click',function(overlay, latlng, overlaylatlng){
      if (self.draggingOverlay) {
        self.draggingOff();
      }
    })
    this.listeners.push(listener);


    var listener = GEvent.addListener(this.insetMap,'moveend',function(){
      if (self.draggingOverlay) {
        self.draggingOff();
      }
    })
    this.listeners.push(listener);

    return oDiv;
  };

  MDraggablePolys.prototype.draggingOn = function(overlay) {
    this.insetMap.setCenter(this.map.getCenter());

    var points = Array();
    for (var n = 0 ; n < overlay.getVertexCount() ; n++ ) {
      points.push(overlay.getVertex(n));
    }

    this.map.removeOverlay(overlay);
    this.draggingOverlay = overlay;

    if (overlay instanceof GPolyline) {
      this.internalOverlay = new GPolyline(points,'blue',3,1);
    }
    else if (overlay instanceof GPolygon) {
      this.internalOverlay = new GPolygon(points,'blue',3,1,'blue',0.2);
      this.internalOverlay.poly_config = overlay.poly_config;
      this.internalOverlay.poly_id = overlay.poly_id;
      this.internalOverlay.clickFn = overlay.clickFn;
    }

    this.insetMap.addOverlay(this.internalOverlay);

    this.container.style.display = '';
  };

  MDraggablePolys.prototype.draggingOff = function() {
    this.insetMap.removeOverlay(this.internalOverlay);
    this.applyShift();
    this.map.addOverlay(this.draggingOverlay);

    this.container.style.display = 'none';
    this.internalOverlay = null;
    this.draggingOverlay = null;
    this.insetMap.setCenter(this.map.getCenter());
  };

  MDraggablePolys.prototype.applyShift = function() {
    var proj = this.map.getCurrentMapType().getProjection();
    var mapCenterPx = proj.fromLatLngToPixel(this.map.getCenter(),this.zoom);
    var iMapCenterPx = proj.fromLatLngToPixel(this.insetMap.getCenter(),this.zoom);
    var deltaX = mapCenterPx.x-iMapCenterPx.x;
    var deltaY = mapCenterPx.y-iMapCenterPx.y;
    var points = [];


    for (var n = 0 ; n < this.internalOverlay.getVertexCount() ; n++ ) {
      var latlon = this.internalOverlay.getVertex(n);
      var vertexPx = proj.fromLatLngToPixel(latlon,this.zoom);
      newLatLon = proj.fromPixelToLatLng(new GPoint(vertexPx.x + deltaX,vertexPx.y+deltaY),this.zoom);
      points.push(newLatLon);
    }
    var _c = this.internalOverlay.poly_config;
    this.draggingOverlay = new GPolygon(points, _c.line_color, _c.line_size, _c.line_opacity, _c.fill_color, _c.fill_opacity);
    this.draggingOverlay.poly_config = this.internalOverlay.poly_config;
    this.draggingOverlay.poly_id = this.internalOverlay.poly_id;
    this.draggingOverlay.clickFn = this.internalOverlay.clickFn;
    GEvent.addListener(this.draggingOverlay,'click', this.draggingOverlay.clickFn);
  };

  MDraggablePolys.prototype.getDefaultPosition = function() {
    return this.position;
  };

  MDraggablePolys.prototype.createBlankMap = function() {
    var normalProj = G_NORMAL_MAP.getProjection();
    var cMap = new GMapType([], normalProj, 'Blank', {maxResolution:20, minResolution:0, errorMessage:'Boom!'});
    return cMap;
  };

  MDraggablePolys.prototype.remove = function() {
    while (this.listeners.length) {
      var listener = this.listeners.shift();
      GEvent.removeListener(listener);
    }
  };

  var map = new GMap2(document.getElementById('map'));
  map.setCenter(new GLatLng(3.5, 3.5), 6);

  bm = new BonMapza(map, {
    click: function(ov,point) {

    },
    ob: function(points) {
      i++;
      document.getElementById('lines').innerHTML += "<br>Обводка №" + i + ": Точек " + points.length;
    }
  });

  line = bm.createLine([
     {lat:7, lng:0, line_size:1}
    ,{lat:6, lng:3, draggable:true, marker: true}
    ,{lat:5.5, lng:6, line_size:20, line_opacity: 0.45, draggable:true}
    ,{lat:3, lng:6.1, line_color: "#ff0000", draggable:true, marker: true}
    ,{lat:0, lng:6.2, marker: true, click:function(latlng){
      map.openInfoWindowHtml(latlng, "Этот маркер подвинуть нельзя!");
    }}
  ], {
    line_opacity: 1
  });

  bm.createPolygon([
    {lat: 0, lng:0}
   ,{lat: 5, lng:0}
   ,{lat: 5, lng:5}
   ,{lat: 0, lng:5}
   ,{lat: 0, lng:0}
  ], {
    line_color: "#ff0000",
    line_size:3,
    line_opacity: 1,
    fill_color: "#ff0000",
    fill_opacity: 0.45,
    click:function(latlng){
      map.openInfoWindowHtml(latlng, "Для перемещения полигона<br>кликните на него с нажатым Ctrl");
    }
  });
}

function getpoint() {
  document.getElementById('points').innerHTML = "Получено и сохранено " + bm.getLinePoints(line).length + " точек";
}

$(document).ready(init);
