" =============================================================================
" Filename: autoload/calendar/text.vim
" Author: itchyny
" License: MIT License
" Last Change: 2015/03/29 06:32:06.
" =============================================================================

let s:save_cpo = &cpo
set cpo&vim

" Text object, string with a position to display, and syntaxes.
"   s: The string (if it is a string) or the length of spaces (if it is a number)
"   x: Position x
"   y: Position y
"   syn: The syntax string (For example, Sunday, Saturday and Cursor)
fu! calendar#text#new(s, x, y, syn) abort
  retu extend(copy(s:self),{'s':a:s,'x':a:x,'y':a:y,'t':!type(a:s),'syn':[[a:syn,a:y,a:x,a:x+(type(a:s)?len(a:s):a:s),0]]})
endfu

let s:self = {}

" Move all the things.
fu! s:self.move(x, y) dict abort
  let self.x += a:x
  let self.y += a:y
  cal map(self.syn, '[v:val[0],v:val[1]+a:y,v:val[2]+a:x,v:val[3]+a:x,v:val[4]]')
  retu self
endfu

" Extend the syntaxes.
fu! s:self.height(h) dict abort
  for s in self.syn
    if a:h
      let s[4] = a:h
    en
  endfo
  retu self
endfu

" Split all the extend the syntaxes.
fu! s:self.split() dict abort
  let syn = []
  for s in self.syn
    if s[4]
      let t = s
      let l = s[4]
      let s[4] = 0
      wh l > 1
        let t = copy(t)
        let t[1] += 1
        cal add(syn, t)
        let l -= 1
      endw
    en
  endfo
  retu map(syn, 'calendar#text#new(v:val[3] - v:val[2], v:val[2], v:val[1], v:val[0])')
endfu

" :h version7 | /7.2.061
silent! call calendar#string#strdisplaywidth('')

let s:W = function((exists('*strdisplaywidth') ? '' : 'calendar#string#') . 'strdisplaywidth')
let s:T = function('calendar#string#truncate')
let s:R = function('calendar#string#truncate_reverse')

fu! s:self.concat(t) dict abort
  let s = self.syn
  let t = a:t.syn
  let q = range(len(s))
  let l = s:T(self.s, a:t.x)
  let r = s:R(self.s, s:W(self.s) - s:W(a:t.s) - a:t.x)
  let x = len(l) - a:t.x
  let e = len(l) + len(a:t.s)
  let f = len(self.s) - len(r)
  let y = min([e, f])
  let m = e - f
  if !a:t.t
    let self.s = l . a:t.s . r
  en
  if x
    cal s:shift(t, x)
  en
  for i in q
    if s[i][2] >= y | let s[i][2] += m | en
    if s[i][3] >= y | let s[i][3] += m | en
  endfo
  retu x
endfu

fu! s:shift(t, x) abort
  let p = range(len(a:t))
  for i in p
    let a:t[i][2] += a:x
    let a:t[i][3] += a:x
  endfo
endfu

fu! s:over(j, v, u) abort
  let [s, d] = [[], []]
  let v = a:v
  let u = a:u
  if u[2] < v[2]
    if u[3] <= v[3]
      let u[3] = v[2]
    el
      let syn = copy(u)
      let u[3] = v[2]
      let syn[2] = v[3]
      cal add(s, syn)
    en
  el
    if u[3] <= v[3]
      cal add(d, a:j)
    el
      let u[2] = v[3]
    en
  en
  retu [s, d]
endfu

" Text piling, the most important method of text object.
fu! s:self.over(t) dict abort
  let s = self.syn
  let t = a:t.syn
  let x = a:t.t ? 0 : self.concat(a:t)
  if len(s) == 1 && s[0][0] == ''
    let self.syn = t
    retu x
  en
  let p = range(len(t))
  for i in p
    let d = []
    for j in range(len(s))
      if s[j][3] <= t[i][2]
        con
      elsei s[j][2] >= t[i][3]
        con
      elsei t[i][0] != 'Cursor' && s[j][0] != 'Cursor'
        let [b, c] = s:over(j, t[i], s[j])
        cal extend(s, b)
        cal extend(d, c)
      en
    endfo
    if len(d)
      for n in reverse(d)
        cal remove(s, n)
      endfo
    en
  endfo
  cal extend(s, t)
  retu x
endfu

let &cpo = s:save_cpo
unlet s:save_cpo