w = 3;
zoom = 2;
maxPointnum = new Array;
maxPointnum[0]  = 0;  //  0: グリフ無し
maxPointnum[1]  = 2;  //  1: 直線    [始点, 終点]
maxPointnum[2]  = 3;  //  2: 曲線    [始点, 制御点, 終点]
maxPointnum[3]  = 3;  //  3: 折れ    [始点, カド, 終点]
maxPointnum[6]  = 4;  //  6: 乙曲線  [始点, 制御点a, 制御点b, 終点]
maxPointnum[7]  = 4;  //  7: 縦払い  [始点, 接続点, 制御点, 終点]
maxPointnum[99] = 2;  // 99: 部品    [左上, 右下]      ただし a[7] は部品名


$(document).ready(function() {
  $('#btn_draw').click(execute);
  $('#btn_svg').click(showSVG);
  $('#width_futo').change(changeStrokeWidth);
  $('#width_hoso').change(changeStrokeWidth);
  $('.radio_startedge').change(changeStartEdge);
});

function changeStartEdge() {
  setStartEdge();
  drawAllStroke();
}

function setStartEdge() {
  startEdge = $('.radio_startedge:checked').val();
}

function execute() {
  reset();
  setStrokeWidth();
  setStartEdge(); // **
  var kage = $('#kage').val().split("\n");
  stroke = new Array;
  for (var i = 0; i < kage.length; i++) {
    var a = kage[i].split(":");
    for (var j = 0; j < a.length; j++) {
      a[j] = parseInt(a[j]);
    }
    stroke[i] = new Stroke(i, a);
  }
  drawAllStroke();
}

function drawAllStroke() {
  for (var i = 0; i < stroke.length; i++) {
    drawStroke(stroke[i]);
  }
}

function Stroke(strokenum, a) {
  $('<div id="stroke_' + strokenum + '" class="stroke"></div>').appendTo("#vbody");
  this.num   = strokenum;    // 筆画の番号
  this.type  = a[0];         // 線種
  this.start = a[1];         // 頭形状
  this.end   = a[2];         // 尾形状

  // 点とマーカーをセット
  this.p = new Array;
  for (var i = 0; i < maxPointnum[this.type]; i++) {
    this.p[i] = new Point(zoom * a[2*i+3], zoom * a[2*i+4]);
    setMarker(this.num, i, this.p[i]);
  }
  this.l = new Array;
  this.r = new Array;
}


function Point(x, y) {
  // 座標。今のところベクトルもこれで兼用
  this.x = x;
  this.y = y;
  this.toString = function () {
    return "x: " + this.x / zoom + ", y: " + this.y / zoom;
  };
  this.rotate = Point_rotate;
}


function setMarker(strokenum, pointnum, point) {
  // マーカーを作成
  $('<div id="marker_' + strokenum + '_' + pointnum + '" class="marker"></div>')
      .appendTo("#stroke_" + strokenum)
      .css("left", parseInt(point.x) - w)
      .css("top",  parseInt(point.y) - w)
      .draggable({
          containment: '#canvas',
          cursorAt: {top: w, left: w},
          start:  marker_start,
          drag:   marker_drag,
          stop:   marker_stop,
      });
}

function marker_start(e, ui) {
  // ドラッグ開始時
  // ドラッグする筆画・点の番号を取得
  var a = e.target.id.split("_");
  dragPointnum = a.pop();
  dragStrokenum = a.pop();
}

function marker_drag(e, ui) {
  // ドラッグ中
  stroke[dragStrokenum].p[dragPointnum] = marker_getPoint(e, ui);  // 点の位置を取得
  drawStroke(stroke[dragStrokenum]);  // この筆画を更新
}

function marker_stop(e, ui) {
  // ドラッグ中終了時
}

function marker_getPoint(e, ui) {
  // マーカーの座標を取得
  return new Point(ui.absolutePosition.left - $('#svgcanvas').offset().left + w,
                   ui.absolutePosition.top - $('#svgcanvas').offset().top + w);
}


function getNvector(p1, p2) {
  // p1→p2 に対する単位法線ベクトルを求める
  var dx = p2.x - p1.x;
  var dy = p2.y - p1.y;
  var k = Math.sqrt(dx * dx + dy * dy);
  return new Point(dy / k, -dx / k);
}


function changeStrokeWidth() {
  // 線幅が変更されたとき
  // StrokeWidth を改めてセットし、すべての筆画を描画しなおす
  setStrokeWidth();
  drawAllStroke();
}


function setStrokeWidth() {
  // 筆画の幅を設定
  strokeWidth = new Array;
  strokeWidth['futo'] = parseFloat($('#width_futo').val());
  strokeWidth['hoso'] = parseFloat($('#width_hoso').val());
  strokeWidth['chuu'] = (strokeWidth['futo'] + strokeWidth['hoso']) / 2;
}


function reset() {
  // 筆画をすべて破棄
  $("#vbody").empty();
  $("svg").empty();
}


function showSVG() {
  $('#svgout').val($('div#svgcanvas').html());
}


function drawStroke(s) {
  switch (s.type) {

    case 1:  // 直線
      s.p[0].nv = s.p[1].nv = getNvector(s.p[0], s.p[1]);
      s.tate = (Math.abs(s.p[0].nv.x) > Math.abs(s.p[0].nv.y));  // 縦画か横画か
      var w = (s.tate) ? [ strokeWidth['futo'], strokeWidth['futo'] ]
                       : [ strokeWidth['hoso'], strokeWidth['hoso'] ];
      s.lLn = new Line();
      s.rLn = new Line();
      for (var i = 0; i < 2; i++) {
        s.lLn.p[i] = new Point(s.p[i].x + s.p[i].nv.x * w[i] / 2,
                               s.p[i].y + s.p[i].nv.y * w[i] / 2);
        s.rLn.p[i] = new Point(s.p[i].x - s.p[i].nv.x * w[i] / 2,
                               s.p[i].y - s.p[i].nv.y * w[i] / 2);
      }
      break;

    case 2:  // 曲線
      s.p[0].nv = getNvector(s.p[0], s.p[1]);
      s.p[1].nv = getNvector(s.p[0], s.p[2]);
      s.p[2].nv = getNvector(s.p[1], s.p[2]);
      var w = [(s.start == 7) ? strokeWidth['hoso'] : strokeWidth['futo'],    // 7: 細入り
               strokeWidth['chuu'],
               (s.end   == 7) ? strokeWidth['hoso'] : strokeWidth['futo']];   // 7: 左払い
      s.lSp = new Spline();
      s.rSp = new Spline();
      for (var i = 0; i < 3; i++) {
        s.lSp.p[i] = new Point(s.p[i].x + s.p[i].nv.x * w[i] / 2,
                               s.p[i].y + s.p[i].nv.y * w[i] / 2);
        s.rSp.p[i] = new Point(s.p[i].x - s.p[i].nv.x * w[i] / 2,
                               s.p[i].y - s.p[i].nv.y * w[i] / 2);
      }
      break;

    default:
      break;
  }
  adjustStartEdge(s);
  modifyEnd(s);
  drawStrokeSVG(s);
}

function Spline() {
  this.p = new Array(3);
  this.getT = Spline_getT;
  this.divideSpline = Spline_divideSpline;
  this.getP = Spline_getP;
  this.getQ = Spline_getQ;
}

function Line() {
  this.p = new Array(2);
  this.getT = Line_getT;
  this.divideLine = Line_divideLine;
}


function adjustStartEdge(s) {
//  switch (s.start) {
//    case 32:  // 接続
      if (s.type == 2 && s.start == 32) {
        var lt, rt;
        if (startEdge == 1) {              // 水平
          lt = s.lSp.getT(s.p[0], new Point(1, 0));
          rt = s.rSp.getT(s.p[0], new Point(1, 0));
        } else if (startEdge == -1) {      // 垂直
          lt = s.lSp.getT(s.p[0], new Point(0, 1));
          rt = s.rSp.getT(s.p[0], new Point(0, 1));
        }
        if (lt && rt) {
          s.lSp.divideSpline(lt, 1);
          s.rSp.divideSpline(rt, 1);
        }
      }
//      break;
//    default:
//      break;
//  }
}

function modifyEnd(s) {
  switch (s.type) {
    case 1:  // 直線
      if (s.tate) {  // 縦画
        switch (s.end) {
          case 0:   // 開放
            lt = s.lLn.getT(s.p[1], s.p[1].nv.rotate(20));
            rt = s.rLn.getT(s.p[1], s.p[1].nv.rotate(20));
            s.lLn.divideLine(lt, 0);
            s.rLn.divideLine(rt, 0);
            break;
          default:
            break;
        }
      }
      break;
    case 2:  // 曲線
      if (s.end == 0) {  // 右払い
        var lt, rt;
        lt = s.lSp.getT(s.p[2], s.p[2].nv.rotate(-30));
        rt = s.rSp.getT(s.p[2], s.p[2].nv.rotate(-30));
        s.lSp.divideSpline(lt, 0);
        s.rSp.divideSpline(rt, 0);
      }
      break;
    default:
      break;
  }
}

function Spline_getT(point, v) {
  // point を通り方向が v である直線と、スプラインとの交点（の t の値）を求める
  var t, a, b, c, d, sign;
  var p = this.p;
  a = v.y * (p[0].x - 2 * p[1].x + p[2].x) - v.x * (p[0].y - 2 * p[1].y + p[2].y);
  b = v.y * (p[0].x - p[1].x) - v.x * (p[0].y - p[1].y);
  c = v.y * (p[0].x - point.x) - v.x * (p[0].y - point.y);
  d = b * b - a * c;
  if (d < 0) {  // 解なし
    return null;
  }
  sign = -1;
  return (b + sign * Math.sqrt(d)) / a;
}

function Line_getT(point, v) {
  // point を通り方向が v である直線と、直線との交点（の t の値）を求める
  var t, b, c;
  var p = this.p;
  b = v.y * (p[0].x - p[1].x) - v.x * (p[0].y - p[1].y);
  c = v.y * (p[0].x - point.x) - v.x * (p[0].y - point.y);
  return c / b;
}

function Spline_divideSpline(t, i) {
  // スプラインを p(t) で分割
  //   i = 0 : 区間 [0, t] 部分を新しいスプラインに
  //   i = 1 : 区間 [t, 1] 部分を新しいスプラインに
  var tmp = this.p;
  if (i == 0) {
    tmp[1] = this.getQ(t, 0);
    tmp[2] = this.getP(t);
  } else if (i == 1) {
    tmp[0] = this.getP(t);
    tmp[1] = this.getQ(t, 1);
  }
  this.p = tmp;
}

function Line_divideLine(t, i) {
  // 直線を p(t) で分割
  //   i = 0 : 区間 [0, t] 部分を新しい直線に
  //   i = 1 : 区間 [t, 1] 部分を新しい直線に
  var tmp = this.p;
  if (i == 0) {
    tmp[1] = divide(t, this.p[0], this.p[1]);
  } else if (i == 1) {
    tmp[0] = divide(t, this.p[0], this.p[1]);
  }
  this.p = tmp;
}

function Spline_getP(t) {
  // パラメータが t となる点を求める
  var q0 = this.getQ(t, 0);
  var q1 = this.getQ(t, 1);
  return divide(t, q0, q1);
}

function Spline_getQ(t, i) {
  // 点 qi(t) を求める
  return divide(t, this.p[i], this.p[i+1]);
}

function divide(t, p1, p2) {
  // 線分 p1p2 を t : (1-t) に分割する点を求める
  var x = (1 - t) * p1.x + t * p2.x;
  var y = (1 - t) * p1.y + t * p2.y;
  return new Point(x, y);
}

function Point_rotate(theta) {
  // theta 度だけ反時計回りに回転（y 軸が下向きであることに注意）
  var rad = theta * Math.PI / 180;
  var x = Math.cos(rad) * this.x + Math.sin(rad) * this.y;
  var y = -Math.sin(rad) * this.x + Math.cos(rad) * this.y;
  return new Point(x, y);
}