 
const errMsgCanvasInit = '描画を行うためには、HTML内にcanvasを配置し、idを振って『描画開始』命令に指定します。'

export default {
  // @描画
  '描画開始': { // @描画先にCanvas(文字列でクエリの指定も可)を指定して描画API(2D)の利用準備する // @びょうがかいし
    type: 'func',
    josi: [['の', 'へ', 'で']],
    pure: true,
    fn: function(cv: any, sys: any) {
      if (typeof cv === 'string') { cv = document.querySelector(cv) || document.getElementById(cv) }
      if (!cv) { throw new Error('『描画開始』でCanvasを取得できませんでした。') }
      sys.__addPropMethod(cv)
      sys.__canvas = cv
      sys.__ctx = cv.getContext('2d')
      sys.__fillStyle = 'black'
      sys.__strokeStyle = 'black'
      sys.__setSysVar('描画中キャンバス', cv)
      sys.__setSysVar('描画中コンテキスト', sys.__ctx)
    },
    return_none: true
  },
  '描画中キャンバス': { type: 'const', value: null }, // @ びょうがちゅうきゃんばす
  '描画中コンテキスト': { type: 'const', value: null }, // @ びょうがちゅうこんてきすと
  'キャンバス状態保存': { // @Canvasの状態を保存(save)   // @ きゃんばすじょうたいほぞん
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.save()
    },
    return_none: true
  },
  'キャンバス状態復元': { // @Canvasの状態を復元(restore)   // @ きゃんばすじょうたいふくげん
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.restore()
    },
    return_none: true
  },
  '線色設定': { // @Canvasの線の描画色(lineStyle)を指定する   // @ せんいろせってい
    type: 'func',
    josi: [['に', 'へ']],
    pure: true,
    fn: function(v: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__strokeStyle = v
      if (v !== '') {
        sys.__ctx.strokeStyle = v
      }
    },
    return_none: true
  },
  '塗色設定': { // @Canvasへの描画色(fillStyle)を指定する   // @ ぬりいろせってい
    type: 'func',
    josi: [['に', 'へ']],
    pure: true,
    fn: function(v: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__fillStyle = v
      if (v !== '') {
        sys.__ctx.fillStyle = v
      }
    },
    return_none: true
  },
  '線描画': { // @ [x1, y1]から[x2, y2]まで線を描画する // @ せんびょうが
    type: 'func',
    josi: [['から'], ['へ', 'まで']],
    pure: true,
    fn: function(a: any, b: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.beginPath()
      sys.__ctx.moveTo(a[0], a[1])
      sys.__ctx.lineTo(b[0], b[1])
      sys.__ctx.stroke()
    },
    return_none: true
  },
  '線太設定': { // @ vに線の太さ設定 // @ せんふとさせってい
    type: 'func',
    josi: [['に', 'へ']],
    pure: true,
    fn: function(v: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.lineWidth = v
    },
    return_none: true
  },
  '四角描画': { // @ [x, y, w, h]で矩形を描画する // @ しかくびょうが
    type: 'func',
    josi: [['の', 'へ', 'に']],
    pure: true,
    fn: function(b: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.beginPath()
      sys.__ctx.rect(b[0], b[1], b[2], b[3])
      if (sys.__fillStyle !== '') { sys.__ctx.fill() }
      if (sys.__strokeStyle !== '') { sys.__ctx.stroke() }
    },
    return_none: true
  },
  '全描画クリア': { // @ 描画中のキャンバスをクリアする。 // @ ぜんびょうがくりあ
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.clearRect(0, 0,
        sys.__canvas.width, sys.__canvas.height)
    },
    return_none: true
  },
  '描画クリア': { // @ [x, y, w, h]の範囲を描画クリア。空配列を指定すると『全描画クリア』と同じ。2要素の配列だと[0,0]を省略したのと同じ。 // @ びょうがくりあ
    type: 'func',
    josi: [['の', 'へ', 'に']],
    pure: true,
    fn: function(b: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      if (!(b instanceof Array)) { b = [] }
      if (b.length === 0) {
        b = [0, 0, sys.__canvas.width, sys.__canvas.height]
      } else if (b.length <= 2) {
        b.unshift(0)
        b.unshift(0)
      }
      sys.__ctx.clearRect(b[0], b[1], b[2], b[3])
    },
    return_none: true
  },
  '描画クリップ': { // @ 描画命令のあとに使うと以後その範囲のみに描くことができるようになる // @ びょうがくりっぷ
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.clip()
    },
    return_none: true
  },
  '円描画': { // @ [x, y]へrの円を描画する // @ えんびょうが
    type: 'func',
    josi: [['へ', 'に'], ['の']],
    pure: true,
    fn: function(xy: any, r: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.beginPath()
      sys.__ctx.arc(xy[0], xy[1], r, 0, 2 * Math.PI, false)
      if (sys.__fillStyle !== '') { sys.__ctx.fill() }
      if (sys.__strokeStyle !== '') { sys.__ctx.stroke() }
    },
    return_none: true
  },
  '楕円描画': { // @ [x, y, x幅, y幅, 回転, 開始角, 終了角, 左回転か]に楕円を描画する // @ だえんびょうが
    type: 'func',
    josi: [['へ', 'に', 'の']],
    pure: true,
    fn: function(args: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      if (!args) { throw new Error('楕円描画の引数配列が無効です') }
      if (args.length < 4) { throw new Error('楕円描画の引数配列が不足しています') }
      if (args.length < 7) {
        if (!args[4]) { args[4] = 0 }
        if (!args[5]) { args[5] = 0 }
        if (!args[6]) { args[6] = Math.PI * 2 }
        if (!args[7]) { args[7] = true }
      }
      sys.__ctx.beginPath()
      sys.__ctx.ellipse(...args)
      if (sys.__fillStyle !== '') { sys.__ctx.fill() }
      if (sys.__strokeStyle !== '') { sys.__ctx.stroke() }
    },
    return_none: true
  },
  '多角形描画': { // @ 座標配列vを指定して多角形を描画する // @ たかっけいびょうが
    type: 'func',
    josi: [['で', 'の', 'を']],
    pure: true,
    fn: function(a: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.beginPath()
      const p = a[0]
      sys.__ctx.moveTo(p[0], p[1])
      for (let i = 1; i < a.length; i++) {
        const t = a[i]
        sys.__ctx.lineTo(t[0], t[1])
      }
      sys.__ctx.lineTo(p[0], p[1])
      if (sys.__fillStyle !== '') { sys.__ctx.fill() }
      if (sys.__strokeStyle !== '') { sys.__ctx.stroke() }
    },
    return_none: true
  },
  '画像読': { // @ 画像のURLを読み込んでImageオブジェクトを返す。(URLにdataスキームも指定可能) // @ がぞうよむ
    type: 'func',
    josi: [['の', 'を']],
    pure: true,
    fn: function(url: any, sys: any) {
      const img = new window.Image()
      img.src = url
      img.crossOrigin = 'Anonymous'
      return img
    }
  },
  '画像読待': { // @ 画像のURLを読み込んでImageオブジェクトを返す。その際、画像の読み込みが終わるまで待つ。// @ がぞうよみまつ
    type: 'func',
    josi: [['の', 'を']],
    pure: true,
    asyncFn: true,
    fn: function(url: any) {
      return new Promise((resolve, reject) => {
        const img = new window.Image()
        img.src = url
        img.crossOrigin = 'Anonymous'
        img.onload = () => { resolve(img) }
        img.onerror = () => {
          reject(new Error(`『画像読待』で読込みエラー。URL=『${url}』`))
        }
      })
    }
  },
  '画像逐次読': { // @ (非推奨) 画像のURLを読み込んでImageオブジェクトを返す。また完了時『対象』にも代入する。『逐次実行』構文で使う。 // @ がぞうちくじよむ
    type: 'func',
    josi: [['の', 'を']],
    pure: true,
    fn: function(url: any, sys: any) {
      if (sys.resolve === undefined) { throw new Error('『画像逐次読』は『逐次実行』構文で使ってください。') }
      sys.resolveCount++
      const img = new window.Image()
      img.src = url
      img.crossOrigin = 'Anonymous'
      img.onload = () => {
        sys.__setSysVar('対象', img)
        sys.resolve()
      }
      img.onerror = () => {
        sys.__setSysVar('対象', '')
        sys.reject()
      }
      return img
    }
  },
  '画像読時': { // @ 画像のURLを読み込んでコールバック関数Fを読み込み、変数『対象』にImageオブジェクトを代入する // @ がぞうよんだとき
    type: 'func',
    josi: [['で'], ['の', 'を']],
    pure: true,
    fn: function(f: any, url: any, sys: any) {
      // 関数オブジェクトを得る
      const func = sys.__findVar(f, null) // 文字列指定なら関数に変換
      // 画像を読む
      const img = new window.Image()
      img.src = url
      img.crossOrigin = 'Anonymous'
      img.onload = () => {
        sys.__setSysVar('対象', img)
        func(sys)
      }
      img.onerror = () => {
        sys.__setSysVar('対象', '')
        func(sys)
      }
    },
    return_none: true
  },
  '画像描画': { // @ 画像IMG(またはURL)を描画先座標[x,y]へ描画し、Imageオブジェクトを返す。座標には2,4,8個の引数を指定可能。 // @ がぞうびょうが
    type: 'func',
    josi: [['の', 'を'], ['へ', 'に']],
    pure: true,
    fn: function(img: any, xy: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      const drawFunc = (im: any, ctx: any) => {
        if (xy.length === 2) {
          ctx.drawImage(im, xy[0], xy[1])
        } else if (xy.length === 4) {
          ctx.drawImage(im, xy[0], xy[1], xy[2], xy[3])
        } else if (xy.length === 8) {
          ctx.drawImage(im, xy[0], xy[1], xy[2], xy[3], xy[4], xy[5], xy[6], xy[7])
        } else {
          throw new Error('『画像描画』の第二引数の配列要素は2,4,8個のいずれかです。')
        }
      }
      if (typeof img === 'string') {
        const image = new window.Image()
        image.src = img
        image.crossOrigin = 'Anonymous'
        image.onload = () => {
          drawFunc(image, sys.__ctx)
        }
        return image
      } else {
        drawFunc(img, sys.__ctx)
        return img
      }
    },
    return_none: false
  },
  '画像部分描画': { // @ 画像IMG(またはURL)の座標[sx, sy, sw, sh]を描画先座標[dx, dy, dw, dh]へ描画し、Imageオブジェクトを返す // @ がぞうぶぶんびょうが
    type: 'func',
    josi: [['の'], ['を', 'から'], ['へ', 'に']],
    pure: true,
    fn: function(img: any, sxy: any, dxy: any, sys: any) {
      const errArgLen =
        '『画像部分描画』に使える引数は画像と、描画する座標へ2つか、' +
        '描画する座標とその位置の4つか、使用する座標と使用する位置と描画する座標と大きさの8つだけです。'
      if (img && sxy) {
        if (!Array.isArray(sxy) && Array.isArray(img)) { // 逆になっていれば入れ替える
           
          if (typeof sxy === 'string' || String(sxy.__proto__) === '[object HTMLImageElement]') {
            const sw = img
            img = sxy
            sxy = sw
          }
        }
      }

      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      const drawFunc = (im: any, ctx: any) => {
        if (!dxy) {
          if (!sxy) {
            ctx.drawImage(im)
          } else if (sxy.length >= 2) { // もしsxyがあるのにdxyがなかったらdxyを代わりにする
            dxy = sxy
            sxy = undefined
          }
        }
        if (dxy.length === 2) { ctx.drawImage(im, dxy[0], dxy[1]) } else if (dxy.length === 4) {
          if (!sxy) {
            ctx.drawImage(im, dxy[0], dxy[1], dxy[2], dxy[3])
          } else if (sxy.length === 4) {
            ctx.drawImage(im, sxy[0], sxy[1], sxy[2], sxy[3], dxy[0], dxy[1], dxy[2], dxy[3])
          } else { throw new Error(errArgLen) }
        } else { throw new Error(errArgLen) }
      }
      if (typeof img === 'string') {
        const image = new window.Image()
        image.src = img
        image.crossOrigin = 'Anonymous'
        image.onload = () => {
          drawFunc(image, sys.__ctx)
        }
        return image
      } else {
        drawFunc(img, sys.__ctx)
        return img
      }
    },
    return_none: false
  },
  '描画フォント設定': { // @ 描画フォントを指定する(CSSのフォント設定と同じ 例「36px Aria」)。フォントサイズのみの指定も可。 // @ びょうがふぉんとせってい
    type: 'func',
    josi: [['を', 'の', 'で', 'に']],
    pure: true,
    fn: function(n: any, sys: any) {
      // 数値だけならフォントサイズのみの指定
      if (typeof n === 'number') { n = n + 'px sans-serif' }
      // ピクセル数のみの指定なら適当にフォントを足す
      if (/^[0-9]+(px|em)$/.test(n)) {
        n = n + ' sans-serif'
      }
      sys.__ctx.font = n
    },
    return_none: true
  },
  '文字描画': { // @ [x, y]へテキストSを描画する(描画フォント設定でサイズなど指定) // @ もじびょうが
    type: 'func',
    josi: [['へ', 'に'], ['の', 'を']],
    pure: true,
    fn: function(xy: any, s: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.fillText(s, xy[0], xy[1])
    },
    return_none: true
  },
  '文字描画幅取得': { // @ テキストSを指定して文字の描画幅を取得する // @ もじびょうがはばしゅとく
    type: 'func',
    josi: [['の']],
    pure: true,
    fn: function(s: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      return sys.__ctx.measureText(s)
    },
    return_none: false
  },
  '描画起点設定': { // @ 描画位置の起点を[x,y]へ設定する(translate) // @ びょうがきてんせってい
    type: 'func',
    josi: [['へ', 'に']],
    pure: true,
    fn: function(xy: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.translate(xy[0], xy[1])
    },
    return_none: true
  },
  '描画回転': { // @ 描画内容をA度だけ回転する(rotate) // @ びょうがかいてん
    type: 'func',
    josi: [['だけ', 'に', 'へ']],
    pure: true,
    fn: function(a: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.rotate(a * Math.PI / 180)
    },
    return_none: true
  },
  '描画拡大': { // @ 描画内容を[x方向,y方向]だけ拡大する(scale) // @ びょうがかくだい
    type: 'func',
    josi: [['だけ', 'に', 'へ']],
    pure: true,
    fn: function(xy: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.scale(xy[0], xy[1])
    },
    return_none: true
  },
  '描画変換マトリクス設定': { // @ 描画内容を[a,b,c,d,e,f]の変換マトリクスに設定。既存内容を破棄して設定(setTransform) // @ びょうがへんかんまとりくすせってい
    type: 'func',
    josi: [['だけ', 'に', 'へ']],
    pure: true,
    fn: function(a: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.setTransform(a[0], a[1], a[2], a[3], a[4], a[5], a[6])
    },
    return_none: true
  },
  '描画変換マトリクス追加': { // @ 描画内容を[a,b,c,d,e,f]のマトリクスで変換。既存のマトリクスに掛け合わせる(transform) // @ びょうがへんかんまとりくすついか
    type: 'func',
    josi: [['だけ', 'に', 'へ']],
    pure: true,
    fn: function(a: any, sys: any) {
      if (!sys.__ctx) { throw new Error(errMsgCanvasInit) }
      sys.__ctx.transform(a[0], a[1], a[2], a[3], a[4], a[5], a[6])
    },
    return_none: true
  },
  '描画データURL変換': { // @ 描画内容をPNG形式のデータURLに変換して得る。 // @ びょうがでーたURLへんかん
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      const cv = sys.__getSysVar('描画中キャンバス')
      const url = cv.toDataURL('image/png')
      return url
    }
  },
  '描画データBLOB変換': { // @ 描画内容をPNG形式のBLOBオブジェクトに変換して戻す。 // @ びょうがでーたBLOBへんかん
    type: 'func',
    josi: [],
    pure: true,
    asyncFn: true,
    fn: function(sys: any) {
      return new Promise((resolve, reject) => {
        const cv = sys.__getSysVar('描画中キャンバス')
        cv.toBlob((result: any) => { resolve(result) }, 'image/png')
      })
    }
  },
  '描画ダウンロードリンク作成': { // @ 描画内容をPNG形式のデータURLに変換してDOMに設定する。 // @ びょうがだうんろーどりんくさくせい
    type: 'func',
    josi: [['へ', 'に']],
    pure: true,
    fn: function(dom: any, sys: any) {
      if (typeof dom === 'string') { dom = document.querySelector(dom) }
      if (!dom) { throw new Error('『描画ダウンロードリンク作成』でDOMが見当たりません。') }
      const cv = sys.__getSysVar('描画中キャンバス')
      if (!cv) { throw new Error('『描画ダウンロード』で描画中キャンバスが設定されていません。') }
      dom.href = cv.toDataURL('image/png')
      dom.download = 'canvas.png'
    },
    return_none: true
  },
  '描画ダウンロード': { // @ 描画内容をPNG形式のデータURLに変換してダウンロードする。(「クリックした時」などと組み合わせて使う) // @ びょうがだうんろーど
    type: 'func',
    josi: [],
    pure: true,
    fn: function(sys: any) {
      const cv = sys.__getSysVar('描画中キャンバス')
      if (!cv) { throw new Error('『描画ダウンロード』で描画中キャンバスが設定されていません。') }
      const a = document.createElement('a')
      a.href = cv.toDataURL('image/png')
      a.download = 'canvas.png'
      a.click()
      return true
    }
  }
}
