------------------------------------------------------------------
-- @todo:
-- kill ring gc?
local wv = require 'nylon.debug' { name = 'pls-ed' }

local ok,e = pcall(function()
                      require 'NylonOs'
                   end)

if not ok then
   wv.log('abnorm','NylonOs not found')
end

local Nylon = require 'nylon.core'()


local edopts = {
   replaceMark = true
}

local function wait_first_event( cord, handlers )
   
   local rc
   local done = false

   -- setup event handlers
   for nEvent, fHandler in pairs(handlers) do
      -- wv.log('debug','set event handler for nEvent=%s',nEvent)
      cord.event[nEvent] = function(...)
         rc = fHandler(...)
         done = true
      end
   end

   while not done do
      cord:yield_to_sleep()
   end

   -- remove event handlers
   for k, v in pairs(handlers) do
      cord.event[k] = nil
   end

   return rc
end

local killring = {}
local killring_yankndx = 0
local function killring_add( t )
   table.insert( killring, t )
   killring_yankndx = #killring
   if NylonOs then
      NylonOs.Static.setclipboard(killring[#killring])
   end
end
local function killring_append(t)
   killring[#killring] = killring[#killring] .. t
   if NylonOs then
      NylonOs.Static.setclipboard(killring[#killring])
   end
end
local function killring_getyanktext()
   if NylonOs then
      return NylonOs.Static.getclipboard()
   else
      return killring[killring_yankndx]
   end
end

local function watch_cursor_pos( ptTopLeft, thePoint, wdim )
   local charsInto = thePoint - ptTopLeft
   local ptRow, ptCol
   local drow = 0
   local dcol = 0
   local charsDrawn = 0
   return function( l, col, eol )
      local toDraw = eol and (eol - col + 1) or (#l-col+1) -- was #l
      if (charsDrawn + toDraw) > charsInto then
         return (dcol + charsInto-charsDrawn), drow
      else
         charsDrawn = charsDrawn + toDraw
      end
      if eol then
         dcol = 0
         drow = drow + 1
      else
         dcol = dcol + toDraw
      end
   end
end

local Theme = require 'pls-theme'

--# [[View]]
--# A view is completely described by:
--# The ncurses window (and dimensions), buffer, topleft, and point
--# incremental search, e.g., is unique to a view.
-- (and maybe some internal stuff? like "last_col", though that has to do
-- only with _movement/key interaction_ and not display; same might be true
-- of e.g., things like 'last i-search start point" or something).

local function entryfn_edit( cord, env, buffer, opt )
   
   local wdim = opt.wdim
   local w = env.w

   env.on = env.on or {} -- ensure table exists, to make test easier
   env.report = env.report or function() end

   opt.plug = opt.plug or {}

   local wordWrap = false
   local walkFun = wordWrap and buffer.walkFragmentsEOL_orWidth or buffer.walkFragmentsEOL

   -- add refresh every 2s
   if true then
      local refresh_cord = Nylon.cord('refreshedit', function(refcord)
                                     while true do
                                        refcord:sleep(10)
                                        cord.event.refresh()
                                     end
      end )
   end

   local function cursorpos( ptTopLeft, thePoint, wdim )
      local f = watch_cursor_pos(ptTopLeft,thePoint,wdim)
      for l, col, eol in walkFun(buffer, ptTopLeft, wdim.w) do
         local cx, cy = f( l, col, eol )
         if cx then
            return cx, cy
         end
      end
   end
   
   local ptTopLeft = 1
   local thePoint = 1
   local theMark
   local isearch_fstring = nil

   local wasding -- true when last event was a "ding", should prevent status
                 -- update from overriding ding message

   local function get_marked()
      local markbeg = theMark and (thePoint < theMark) and thePoint or theMark
      local markend = theMark and (theMark < thePoint) and thePoint or theMark
      return markbeg, markend
   end

   local function get_line_marked( drawPoint, drawPointAfterLine )
      local markbeg, markend = get_marked()
      local lineMarkBeg, lineMarkEnd 
      if markbeg and drawPointAfterLine > markbeg and drawPoint < markend then
         lineMarkBeg = drawPoint >= markbeg and 1 or (markbeg-drawPoint+1)
         lineMarkEnd = drawPointAfterLine <= markend and (drawPointAfterLine-drawPoint) or (markend - drawPoint)
      end
      return lineMarkBeg, lineMarkEnd
   end

   local function pdcur_draw_window( wdim )
      local charsInto = thePoint - ptTopLeft
      local c = opt and opt.theme and opt.theme.text and Theme[opt.theme.text] or Theme.normal
      w:attron( c )

      local function draw_line( drow, l, markBeg, markEnd )

         w:move(drow+wdim.y,wdim.x)

         local s, e
         if isearch_fstring and #isearch_fstring > 0 then
            if isearch_fstring:find '[A-Z]' then
               s,e = l:find(isearch_fstring) 
            else
               s,e = l:lower():find(isearch_fstring) 
            end
         end
         local ndrawn = #l
         if s then
            local Theme = require 'pls-theme'
            w:addstr( l:sub(1,s-1) )
            w:attroff( c )
            Theme.with.classicmenu( w, function()
                                       w:addstr( l:sub(s,e) )
            end )
            w:attron( c )
            w:addstr( l:sub(e+1,wdim.w) )
         else
            if opt.plug.drawline then
               ndrawn = opt.plug.drawline( w, wdim.x, drow+wdim.y, l, markBeg, markEnd  ) or ndrawn
            else
               if not markBeg then
                  w:addstr((#l > wdim.w) and l:sub(1,wdim.w) or l)
               else
                  local endPoint = markEnd > wdim.w and wdim.w or markEnd
                  w:addstr( l:sub(1,markBeg-1) )
                  local function drawMarked() w:addstr( l:sub(markBeg,endPoint) ) end
                  
                  if opt and opt.theme and opt.theme.text == 'inverse' then
                     -- 160518 bah, this is not working as i expect
                     w:attroff(c)
                     Theme.with.classicmenu( w, drawMarked )
                     w:attron(c)
                  else
--                     w:attroff(c)
                     Theme.with.inverse( w, drawMarked )
--                     w:attron(c)
                  end
                  w:addstr( l:sub(endPoint+1,wdim.w) )
               end
            end
         end

         if ndrawn < wdim.w then
            -- wv.log('debug','eol drawn=%d w=%d', drawn, wdim.w)
            --w:mvaddstr( drow,drawn,string.rep(string.char(test1), (wdim.w-drawn)) )
            w:mvaddstr( drow+wdim.y,wdim.x+ndrawn,string.rep(' ', (wdim.w-ndrawn)) )
         end
      end

      local drow = 0
      local linefrags = {}
      local drawPoint = ptTopLeft

      for l, col, eol in walkFun( buffer, ptTopLeft, wdim.w ) do
         -- wv.log('debug','pdcur_draw_window walk win=%dx%d@%d,%d #l=%d d=%d,%d text col=%d eol=%d', wdim.w, wdim.h, wdim.x, wdim.y, #l, drow, dcol, col or -99, eol or -99)

         if eol then
            table.insert( linefrags, l:sub(col,eol-1) )
            local wholeLine = table.concat(linefrags)

            local drawPointAfterLine = drawPoint + #wholeLine + 1
            local lineMarkBeg, lineMarkEnd = get_line_marked( drawPoint, drawPointAfterLine )
            draw_line( drow, wholeLine, lineMarkBeg, lineMarkEnd )
            drawPoint = drawPointAfterLine
            drow = drow + 1
            linefrags = {}
         elseif col > 1 then
            table.insert( linefrags, l:sub(col) )
         else
            table.insert( linefrags, l )
         end

         if drow >= wdim.h then
            break
         end
      end
      if #linefrags > 0 then
         draw_line( drow, table.concat(linefrags) )
         drow = drow + 1
      end
      while drow < wdim.h do
         w:mvaddstr(drow+wdim.x,wdim.y,string.rep(' ',wdim.w))
         drow = drow + 1
      end
      local cx,cy=cursorpos(ptTopLeft,thePoint,wdim)
--      if cx > wdim.w then -- hack!
--         local exceeded = (cx-wdim.w)+5
--         local str = w:mvgetstr( cy, exceeded )
--         wv.log('debug','exceeded screen cx=%d wdim.w=%d str=%s', cx, wdim.w, str)
--         w:mvaddstr( cy, 0, str:sub(1,wdim.w) )
--      end
      w:attroff(c)
      if isearch_fstring then
         env.report('isearch','i-search: ' .. isearch_fstring)
      else
         --local ndx,col = buffer:lcol4point(thePoint)
         if not wasding then
            env.report( 'navstatus', string.format('sz=%d pt=%d top=%d x,y=%02d,%02d',
                                                   buffer:end_point(), thePoint, ptTopLeft, cx or -1, cy or -1))
         end
      end
   end

   local keybindings  = (require 'pls-keys').edit

   local function ding(msg,p,...)
      Pdcurses.Static.beep()
      if msg then
         wasding = true
         env.report('oops',p and string.format(msg,p,...) or msg)
      end
   end

   local function report(...)
      env.report(...)
      wasding = true
   end


   local function next_isearch_forward( startPoint )
      local nextpoint = buffer:search_forward_to( startPoint, isearch_fstring )
      wv.log('debug','isearch got nextpoint=%s pt=%d', nextpoint, thePoint)
      if nextpoint then
         thePoint = nextpoint
         local cx, cy = cursorpos(ptTopLeft,thePoint,wdim)
         if cy >= wdim.h then -- ran off screen
            local adjust = math.floor(cy - (wdim.h / 3)) -- put next point at top 1/3 of screen
            wv.log('debug','isearch moved beyond screen! wdim.h=%d cy=%d adjust=%d', wdim.h, cy, adjust)
            local st = buffer:forward_lines(ptTopLeft, adjust)
            if st then
               ptTopLeft = st
            end
         end
      else
         ding 'i-search failed'
      end
      cord.event.refresh()
   end

   -- Warning!! This function does not communicate back with input handling framework.
   -- for the actual key handler look at the setup in [main.lua::make_editor()]
   cord.event.key = function(k)
      wv.log('debug','edit got key=%s => %s', k, keybindings[k] )

      if keybindings[k] then
         cord.event[ keybindings[k] ]()
      elseif type(k) == 'number' then
         if k < 256 and k >= 32 then -- don't insert meta chars and ctrl chars
            if isearch_fstring then
               isearch_fstring = isearch_fstring .. string.char(k)
               next_isearch_forward( thePoint )
               cord.event.refresh()
            else
               cord.event.text(k) 
            end
         elseif k == 27 then -- don't insert meta chars and ctrl chars
            if isearch_fstring then
               isearch_fstring = nil
               cord.event.refresh()
            else
               return k
            end
         else
            return k
         end
      end
   end
   
   local baseline = 1
   local ccol = 1
   local crow = 1

   local function cursor_line()
      return buffer[crow+baseline-1] or ''
   end

   local function char_at_point()
      return buffer:char_at_point(thePoint)
   end



   local function insert_at_point(t)
      -- wv.log('debug','insert at pt=%d str=[%s]',thePoint, t)
      buffer:insert( thePoint, t )
      thePoint = thePoint + #t
      if env.on.modified then
         env.on.modified()
      end
--      local l = cursor_line()
--      if ccol > #l+1 then
--         ccol = #l+1
--      end
--      buffer[crow+baseline-1] = l:sub(1,ccol-1) .. t .. l:sub(ccol)
--      ccol = ccol + #t
   end

   function cord.event.getPoint(ret)
      ret(thePoint)
   end

   function cord.event.replace_marked(cbfun)
      if cbfun then
         local mb, me = get_marked()
         if mb and me > mb then
            local rc = cbfun( buffer:sub(mb,me-1) )
            if rc ~= false then -- false return code means abort operation
               buffer:remove(mb,me-1)
               buffer:insert(mb,rc or '')
               theMark = nil
               cord.event.refresh()
            end
         else
            cbfun() -- callback with no params to indicate no marked region
         end
      end
   end

   function cord.event.kill_buffer()
      if env.on.kill_buffer then
         env.on.kill_buffer()
      else
         ding 'kill-buffer not implemented'
      end
   end
   function cord.event.undo()
      local undopoint = buffer:undo() 
      if undopoint then
         thePoint = undopoint
         if env.on.modified then
            env.on.modified()
         end
         cord.event.refresh()
      else 
         ding 'No further undo information'
      end
   end

   function cord.event.isearch_forward()
      -- ding 'isearch_forward not implemented'
      -- emacs search forward uses 'ctrl-g' to clear broken chars back to
      -- last match, and esc exits the search altogether

      if isearch_fstring then -- search for next match
         next_isearch_forward( thePoint + 1 )
      else -- start new search 
         isearch_fstring = ''
      end
      cord.event.refresh()
   end
   
   function cord.event.ctrlg()
      isearch_fstring = nil
      theMark = nil
      cord.event.refresh()
   end


   function cord.event.isearch_backward()
      ding 'isearch_backward not implemented'
   end

   function cord.event.jumptotag()
      local w = buffer:word_at_point(thePoint > 1 and (thePoint-1) or thePoint)
      local w2 = buffer:word_at_point( buffer:end_of_line(thePoint)-1 )
      local _ = env.on.tagActivated and env.on.tagActivated( w, w2 )
   end

   function cord.event.query_citations()
      local _ = env.on.queryCitations and env.on.queryCitations()
   end
   
   function cord.event.save_buffer()
      if env.on.save then
         report('major','Saving buffer')
         local one = buffer:asOneString()
         if true then -- create temp record to save my sanity maybe
            local f = io.open(string.format('/tmp/pls-r-%f.txt',Nylon.uptime()),"w")
            f:write(one)
            f:close()
         end
         env.on.save( one )
         report('major','Buffer saved')
      else
         report('warn','No buffer save handler')
      end
   end

   function cord.event.insert_at_point( t )
      insert_at_point( t )
      cord.event.refresh()
   end

   local function do_yank( special )
      if env.on.yank and env.on.yank( special ) then
         return
      end
      local yanktext = killring_getyanktext() 
      insert_at_point( yanktext )
      cord.event.refresh()
   end

   function cord.event.yank()
      do_yank()
   end

   
   function cord.event.special_yank() -- typically allows paste of html
      do_yank(true)
   end

   function cord.event.text(t)
      if t < 256 then
         local theString = string.char(t)
         if theMark and edopts.replaceMark then
            local markbeg = get_marked()
            cord.event.replace_marked( function()
                                         thePoint = markbeg + 1
                                         return theString
            end)
         else
            insert_at_point(theString)
         end
         save_col = true
      else
         ding("unrecognized key=%d",t)
      end
      cord.event.refresh()
   end

   function cord.event.indent_for_tab_command()
      insert_at_point('   ')
      cord.event.refresh()
   end


   function cord.event.delete_char()
      buffer:remove(thePoint)
      cord.event.refresh()
   end

   local lastkill

   local function killring_addorappend(t)
      wv.log('debug','killring_addorappend lastkill=%s',lastkill)
      if lastkill then
         killring_append(t)
      else
         killring_add(t)
      end
   end

   function cord.event.kill_line()
      if buffer:char_at_point(thePoint) == '\n' then
         buffer:remove(thePoint)
         killring_addorappend '\n'
      else
         local eolPoint = buffer:end_of_line(thePoint)
         if buffer:char_at_point(eolPoint) == '\n' then
            eolPoint = eolPoint - 1
         end
         if eolPoint ~= thePoint then
            local deleted = buffer:sub( thePoint, eolPoint )
            buffer:remove( thePoint, eolPoint )
            killring_addorappend( deleted )
         end
      end
      cord.event.refresh_kill()
   end

   function cord.event.escape()
      if isearch_fstring then
         isearch_fstring = nil
         cord.event.refresh()
      end
      if opt.oneLine then
         wv.log 'oneLine edit got escape (cancel)'
         local _ = env.on.cancelled and env.on.cancelled()
      end
   end

   function cord.event.newline()
      if opt.oneLine then
         if env.on.save then
            env.on.save( buffer:asOneString() )
         end
      else
         buffer:insert(thePoint,'\n')
         thePoint = thePoint+1

         local cx, cy = cursorpos( ptTopLeft, thePoint, wdim )
         if cy and (cy + 1 > wdim.h) then
            local nextline = buffer:search_forward_to( ptTopLeft, '\n' )
            if nextline then 
               ptTopLeft = nextline + 1
            end
         end

         cord.event.refresh()
      end
--      if ccol <= #cursor_line() then
--         local l = buffer[crow+baseline-1]
--         buffer[crow+baseline-1] = l:sub(1,ccol-1)
--         table.insert(buffer,crow+baseline,l:sub(ccol))
--      else
--         while (crow+baseline) > #buffer do
--            table.insert(buffer,'')
--         end
--         table.insert(buffer,crow+baseline,'')
--      end
--      ccol = 1
--      cord.event.next_line()
   end

   function cord.event.delete_backward_char()
      if isearch_fstring then
         isearch_fstring = isearch_fstring:sub(1,#isearch_fstring-1)
      else
         if thePoint > 1 then
            thePoint = thePoint - 1
            buffer:remove(thePoint)
         else
            ding '(delete back) beginning of buffer'
         end
      end
      cord.event.refresh()
   end

   local last_col
   local save_col

   local function _go_if(newPoint)
      if newPoint and newPoint ~= thePoint then
         thePoint = newPoint
         save_col = true
         return newPoint
      end
   end

   local function lfn_backward_word()
      _go_if( buffer:backward_word(thePoint) )
      cord.event.refresh()
   end
   
   cord.event.backward_word = lfn_backward_word

   local function lfn_delete_word()
      local endword = buffer:forward_word(thePoint) or (buffer:end_point()+1)
      if endword and endword > thePoint then
         local removed = buffer:sub(thePoint,endword-1)
         killring_addorappend(removed)
         buffer:remove(thePoint,endword-1)
         cord.event.refresh_kill()
      else
         ding 'No more words (and no more promises)'
      end
   end

   cord.event.delete_word = lfn_delete_word
   
   function cord.event.context_menu()
      local w = buffer:word_at_point(thePoint > 1 and (thePoint-1) or thePoint)
      wv.log('debug','context_menu word_at_point=%s',w)
      local function consume_word()
         lfn_backward_word()
         lfn_delete_word()
      end
      local _ = env.on.contextmenu and env.on.contextmenu(w,{ consume_word = consume_word })
      cord.event.refresh()
   end
   

   local function restore_col()
      -- wv.log('debug','last_col=%s',tostring(last_col))
      if last_col then
         local eolpoint = buffer:end_of_line(thePoint)
         wv.log('debug','eolpoint=%d thePoint=%d last_col=%d',eolpoint,thePoint,last_col)
         if (eolpoint - thePoint) > last_col then
            thePoint = thePoint + last_col
         else
            thePoint = eolpoint
         end
      end
   end

   function cord.event.set_mark_command()
      theMark = thePoint
      cord.event.refresh()
   end

   local function _copy_marked_and(fn)
      local mb, me = get_marked()
      if me and me > mb then
         local removed = buffer:sub(mb,me-1)
         killring_addorappend(removed)
         if fn then
            fn(mb,me)
         end
         theMark = nil
         cord.event.refresh()
      else
         ding 'No selection - use [C-x Spc] to mark'
      end
   end

   function cord.event.kill_ring_save()
      _copy_marked_and()
   end
   function cord.event.kill_region()
      _copy_marked_and( function(mb,me)
         buffer:remove(mb,me-1)
         thePoint = mb
      end)
   end

   function cord.event.next_line()
      local point = ptTopLeft
      local nextLineFromTop
      local cx, cy
      local thePointStart = thePoint
      local curfun = watch_cursor_pos(ptTopLeft, thePoint, wdim)
      for l, col, nl in walkFun(buffer,ptTopLeft,wdim.w) do
         if not cx then
            cx, cy = curfun( l, col, nl )
         end
         local toDraw = nl and (nl - col + 1) or (#l-col+1)
--         wv.log('debug','nextline #l=%d col=%d nl=%d toDraw=%d',#l, col, (nl or -1),toDraw)
         point = point + toDraw
         if nl then
            if not nextLineFromTop then
               nextLineFromTop = point + 1
--               wv.log('debug','got nextLineFromTop=%d',nextLineFromTop)
            end
            if point > thePoint then

               if wordWrap and point >= (thePoint + wdim.w) then
                  thePoint = thePoint + wdim.w
                  break -- don't restore_col, for now
               else
                  thePoint = point
               end
               
               wv.log('debug','point=%d cy=%d nextLineFromTop=%d',point,cy,nextLineFromTop)
               assert(cy) -- should have found cursor
               if cy + 1 >= wdim.h then
                  ptTopLeft = nextLineFromTop
               end
--               if not wordWrap then
                  restore_col()
--               end
               break
            end
         end
      end
      if thePoint == thePointStart then
         thePoint = buffer:end_point() + 2
      end
      cord.event.refresh()
   end

   function cord.event.previous_line()
      if thePoint == 1 then
         ding 'no previous line'
         return
      end

      nextpoint = buffer:beginning_of_line(thePoint)

      if (nextpoint <= 2) and buffer:char_at_point_dec(1) == 10 then
         -- ugly special case for 1st char is '\n'
         thePoint = 1
         restore_col()
      else
         if nextpoint > 1 then
            local bol = buffer:beginning_of_line(nextpoint-1)
            if bol < ptTopLeft then
               ptTopLeft = bol
            end
            thePoint = bol
            restore_col()
         else
            ding 'no previous line'
         end
      end
      cord.event.refresh()
   end


   local function _backward_char() 
      if thePoint > 1 then
         thePoint = thePoint - 1
         save_col = true
      end
   end


   function cord.event.backward_char()
      if thePoint > 1 then
         _backward_char()
         cord.event.refresh()
      else
         ding 'At beginning of buffer'
      end
   end

   local function _forward_char() 
      local ep = buffer:end_point()
      if thePoint <= ep then
         thePoint = thePoint + 1
         save_col = true
      elseif thePoint == (ep + 1) then
         -- when the buffer doesn't end in '\n', we allow point
         -- to move to buffer end + 2, to allow us to move forward
         -- to the next line.
         if buffer:char_at_point(ep) ~= '\n' then
            thePoint = thePoint + 1
            save_col = true
         end
      else
         ding 'End of buffer'
      end
   end

   function cord.event.forward_char()
      _forward_char()
      cord.event.refresh()
   end

   function cord.event.capitalize_word()
      -- hacky; emacs will search forward to the next word if
      -- not currently at a word.
      local w, bow, eow = buffer:word_at_point(thePoint)
      if bow < thePoint then
         w = w:sub( thePoint-bow+1 )
         bow = thePoint
      end
      if w then
         buffer:replace( bow, bow, w:sub(1,1):upper() )
         buffer:replace( bow+1, eow, w:sub(2):lower() )
      end
      cord.event.forward_word()
   end

   function cord.event.upcase_word()
      local w, bow, eow = buffer:word_at_point(thePoint)
      if w then
         buffer:replace( bow, eow, w:upper() )
      end
      cord.event.forward_word()
   end

   function cord.event.downcase_word()
      local w, bow, eow = buffer:word_at_point(thePoint)
      if w then
         buffer:replace( bow, eow, w:lower() )
      end
      cord.event.forward_word()
   end

   local function _forward_to(pat)
      return _go_if( buffer:search_forward_to(thePoint,pat) )
   end
   local function _forward_past(pat)
      return _go_if( buffer:search_forward_past(thePoint,pat) )
   end
   local function _backward_to(pat)
      return _go_if(buffer:search_backward_to(thePoint,pat))
   end
   local function _backward_past(pat)
      return _go_if(buffer:search_backward_past(thePoint,pat))
   end


   function cord.event.move_beginning_of_line()
      _go_if( buffer:beginning_of_line(thePoint) )
      cord.event.refresh()
   end
   
   function cord.event.move_end_of_line() 
            local eol = buffer:end_of_line(thePoint)
            wv.log('debug','move_end_of_line eol=%d',eol)
            if eol == buffer:end_point() then
               eol = eol + 1
            end
            _go_if( eol )
      cord.event.refresh()            
   end
   
   function cord.event.forward_word()
      local fw = buffer:forward_word(thePoint) or (buffer:end_point() + 1)
--      wv.log('debug','forward_word: fw=%d thePoint=%d', fw, thePoint)
      _go_if( fw )
      cord.event.refresh()
   end


   function cord.event.scroll_down_command()
      local start =  (thePoint < buffer:end_point()) and thePoint or (buffer:end_point()-1)
      local prior = buffer:backward_lines(start,wdim.h)
      if prior and prior ~= thePoint then
         thePoint = prior
         restore_col()
         local st = buffer:backward_lines(ptTopLeft,wdim.h)
         if st then ptTopLeft = st end
      else
         ding()
      end
      cord.event.refresh()
   end

   function cord.event.scroll_up_command()
      local s = NylonSysCore.uptime()
      local nextpoint = buffer:forward_lines(thePoint,wdim.h)
      -- env.report('dbg',string.format('nextpoint=%d',nextpoint))
      if nextpoint and nextpoint ~= startPoint then
         thePoint = nextpoint
         restore_col()
         local st = buffer:forward_lines(ptTopLeft,wdim.h)
         if st then
            ptTopLeft = st
         end
      end
      local e = NylonSysCore.uptime()
      wv.log('debug','time to page down=%f',(e-s))
      cord.event.refresh()
   end
   
   function cord.event.beginning_of_buffer()
      ptTopLeft = 1
      thePoint = 1
      save_col = true
      cord.event.refresh()
   end
   
   function cord.event.end_of_buffer()
      local b = NylonSysCore.uptime()
      local ep = buffer:end_point()
      local top = buffer:backward_lines(ep,wdim.h-2)
      if top then
         local prevline = buffer:backward_lines(top-1,1)
         wv.log('debug','eob top=%s prevl=%s',top,prevline)
         if prevline then
            ptTopLeft = prevline
         else
            ptTopLeft = 1
         end
      else
         ptTopLeft = 1
      end
      thePoint = ep + (buffer:char_at_point_dec(buffer:end_point())==10 and 1 or 2)
      save_col = true
      local e = NylonSysCore.uptime()
      wv.log('debug','time for end_of_buffer=%f ptl=%d',(e-b),ptTopLeft)
      cord.event.refresh()
   end
   
   local function do_redraw()
      pdcur_draw_window( wdim )
      local curx,cury = cursorpos(ptTopLeft,thePoint,wdim)
      if curx then
         w:move(wdim.y+cury, wdim.x+curx)
      else
         local ep = buffer:end_point()
         if thePoint > ep then
            local curx, cury = cursorpos( ptTopLeft, ep, wdim)
            cury = cury or 0
            curx = curx or 0
            if (thePoint > ep+1) or buffer:char_at_point(ep)=='\n' then
               w:move( wdim.y+cury+1, wdim.x )
            else
               w:move( wdim.y+cury, wdim.x+curx+1 )
            end
         end
--          curx = curx or 0
--          cury = cury or 0
--          local diff = thePoint-buffer:end_point()
--          curx = curx + diff + (buffer:end_point()>0 and 1 or 0)
--          w:move(wdim.y+cury,curx)
         env.report( 'warn', string.format('sz=%d pt=%d tl=%d (end of buffer)', ep, thePoint, ptTopLeft ))
      end
      if save_col then
         last_col = curx
         save_col = false
      end
   end
      --local showcur = ccol > (#cursor_line()+1) and (#cursor_line()+1) or ccol
      -- w:move( wdim.y + crow -1, wdim.x + showcur -1 )
   
   cord.event.redraw = function(ondone)
      wv.log('debug','window redraw, %dx%d@%d,%d', wdim.w, wdim.h, wdim.x, wdim.y)
      do_redraw()
      w:refresh()
      w:redraw()
      if ondone then
         ondone()
      end
   end

   while true do 
      do_redraw()
      w:refresh()
      local waskill = false
      wasding = false
      wait_first_event( cord, {
         refresh=function( force ) 
            if force then
               w:redraw()
            end
         end,
         refresh_kill=function() 
            waskill = true
         end,
      })
      -- wv.log('debug','cord got event, lastkill=%s waskill=%s',lastkill,waskill)
      lastkill = waskill
   end
end


return {
   entryfn_edit = entryfn_edit
}