-- graphical user interface for "movdump" CLI
-- this lua script requires the guiscript runtime

-- and a tools folder including the following files
-- ffmpeg, mplayer

lvcodec = {"Auto", "avc1", "mp4v", "icod", "xdvc", "MPG2", "jpeg"}
lacodec = {"Auto", "mp4a", "twos", "sowt", "raw "}	
lfps = { "Auto", 29.97, 24 }
lwidth = { "Auto", 1920, 1440, 1280, 640 }
lheight = { "Auto", 1080, 720, 480, 360 }
lsamplerate = { "Auto", 48000, 44100, 22050, 16000, 8000 }
lchannels = { "Auto", 2, 1 }
lsamplesize = { "Auto", 16, 8 }
-- lasmin = {"Auto", 4000, 16000, 32000, 92160 }
lpreview = {"mplayer", "QuickTime Player" }
lencode = {"ffmpeg (mov)", "mencoder (avi)"}

files = {}
consolefile = ""
outputdir = ""
file = ""
reffile = ""
stopaction = false
proc = nil
timeractive = false
APPNAME = "Video Repair Tool v1.5 (www.grauonline.de)"
PATHSEP = gs.pathsep()
SCRIPTFOLDER = gs.scriptfolder()
TOOLSFOLDER = SCRIPTFOLDER .. "tools" .. gs.platform() .. PATHSEP
WEBCLIENT = "open"
EDITOR = "open"
EXEEXT = ""
REDIR_STDERR_NULL = "2> /dev/null"
if (gs.platform() == "win") then   
  EXEEXT = ".exe"
  EDITOR = "notepad"
  REDIR_STDERR_NULL = "2> NUL"
  WEBCLIENT = "explorer"
end


function quotefile(file) 
  if (gs.platform() == "win") then
    return ('"' .. file .. '"')    
  else
    return ("'" .. file .. "'")
  end
end

function timercall()            
  --print("timer called")
  if timeractive then return end
  if proc == nil then return end
  timeractive = true
  local s = ""
  if stopaction then  
    print("closing")
    gs.closepipe(proc)    
  else
    s = gs.readpipe(proc, 80)
    -- print("LINE=" .. s)
  end
  if (s == "") then         
    proc=nil
    print("END")  
    gs.setlabel(labstatus, "Preview finished.")        
  end  
  timeractive = false  
end

function ellipsepath(s)
  local r = ""
  local times = 0
  local c = 0
  local append = ""
  for token in string.gmatch(s, "[^" .. PATHSEP .. "]+") do
    --print ("token:" .. token)
    if (#r < 20) then 
      if ((c == 0) and (string.sub(s, 1, 1) ~= "/")) then
        r = r .. token      
      else
        r = r .. PATHSEP .. token      
      end
    else 
      append = token  
      times = times + 1      
    end  
    c = c + 1 
  end
  if (append ~= "") then 
    if (times == 1) then 
      r = r .. PATHSEP .. append
    else
      r = r .. PATHSEP .. "..." .. PATHSEP .. append 
    end  
  end    
  return r
end

function saythankyou()
  gs.exec(WEBCLIENT .. ' http://grauonline.de/cmsimple2_6/en/?Solutions:HD_Video_Repair_Utility#thankyou')
end

function truncfile(size)
  local fc = gs.splitfilename(file)
  local outfile = outputdir .. fc[2] .. "_truncated" .. fc[3]   
  print(outfile)
  local inp = assert(io.open(file, "rb"))  
  local out = assert(io.open(outfile, "wb"))
  local block = 2048
  local count = 0
  gs.setlabel(labstatus, "Creating truncated movie - Please wait...")          
  while true do
    local bytes = inp:read(block)
    if not bytes then break end
    count = count + block
    if (count >= size) then break end
    out:write(bytes)
  end
  out:close()
  gs.setlabel(labstatus, "Creating truncated movie finished.")          
  print("done")
end

function openlogfile()
  if not gs.fileexists(consolefile) then return end
  print (consolefile)
  gs.exec(EDITOR .. ' ' .. quotefile(consolefile))
end

function clearlogfile()
  if (consolefile == "") then return end
  if not gs.fileexists(consolefile) then return end
  print (consolefile)
  gs.deletefile(consolefile)
end

function choosefile()
  local afile = gs.choosefiledialog{caption="Choose broken movie file"}  
  if (afile == "") then return end
  file = afile
  gs.setlabel(labfile, ellipsepath(file))
  if (outputdir == "") then
    local fc = gs.splitfilename(file)
    outputdir =  fc[1] .. "repaired" .. PATHSEP
    consolefile = fc[1] .. "repaired" .. PATHSEP .. "movdump.txt"
    gs.mkdir(outputdir)
    gs.setlabel(laboutput, outputdir)
    gs.setlabel(labstatus, "Waiting for user to choose reference movie.")
  end
end

function choosefolder()
  local afolder = gs.choosefolderdialog{caption="Choose output folder"}  
  if (afolder == "") then return end
  outputdir = afolder .. PATHSEP 
  consolefile = afolder .. PATHSEP .. "movdump.txt"
  gs.setlabel(laboutput, ellipsepath(outputdir))
end

function choosereffile()
  reffile = gs.choosefiledialog{caption="Choose a non-broken reference movie file"}
  if (reffile == "") then return end
  gs.setlabel(labreffile, ellipsepath(reffile)) 
  gs.setlabel(labstatus, "Waiting for user to start scan.")  
end

function scan()
  print("scan")  
  if (file == "") then return end
  local k, v
  local idx
  gs.setlabel(labstatus, "Scanning - Please wait...")  
  print("outputdir=" .. outputdir)
  local fc = gs.splitfilename(file)
  local outfile = outputdir .. fc[2] .. fc[3]   
  local vfile = outputdir .. fc[2] .. "video.raw"
  local afile = outputdir .. fc[2] .. "audio.raw"
  -- delete existing movies with same name
  lfiles = gs.files(outputdir)  
  for k,v in pairs(lfiles) do 
    local pat = string.gsub(fc[2], '%-', '%%-')
    if (string.match(v, pat) ~= nil) then
      v = outputdir .. v
      print("DELETE " .. v)
      gs.deletefile(v)
    end 
  end   
  -- build CMD
  local cmd = quotefile(SCRIPTFOLDER .. "movdump" .. EXEEXT) .. " -i " .. quotefile(file) .. " -o " .. quotefile(outfile)
  if gs.comboboxindex(cbwidth) ~= 0 then cmd = cmd .. " -fw " .. gs.comboboxvalue(cbwidth) end
  if gs.comboboxindex(cbheight) ~= 0 then cmd = cmd .. " -fh " .. gs.comboboxvalue(cbheight) end  
  if gs.comboboxindex(cbvcodec) ~= 0 then cmd = cmd .. " -vf " .. gs.comboboxvalue(cbvcodec) end
  if gs.comboboxindex(cbacodec) ~= 0 then cmd = cmd .. " -af " .. gs.comboboxvalue(cbacodec) end
  if gs.comboboxindex(cbfps) ~= 0 then cmd = cmd .. " -fps " .. gs.comboboxvalue(cbfps) end
  if gs.comboboxindex(cbsamplerate) ~= 0 then cmd = cmd .. " -ar " .. gs.comboboxvalue(cbsamplerate) end
  if gs.comboboxindex(cbsamplesize) ~= 0 then cmd = cmd .. " -ab " .. gs.comboboxvalue(cbsamplesize) end
  if gs.comboboxindex(cbchannels) ~= 0 then cmd = cmd .. " -ac " .. gs.comboboxvalue(cbchannels) end
  -- if gs.comboboxindex(cbasmin) ~= 0 then cmd = cmd .. " -asmin " .. gs.comboboxvalue(cbasmin) end
  if gs.checkboxvalue(chpcm) ~= 0 then cmd = cmd .. " -pcm " end
  if gs.checkboxvalue(chaac) ~= 0 then cmd = cmd .. " -aac " end
  if gs.checkboxvalue(chnfd) ~= 0 then cmd = cmd .. " -nfd " end
  if gs.checkboxvalue(chraw) ~= 0 then cmd = cmd .. " -ov " .. quotefile(vfile) .. " -oa " .. quotefile(afile) end
  if (reffile ~= "") then cmd = cmd .. " -ref " .. quotefile(reffile) end
  cmd = cmd .. " " .. gs.comboboxvalue(cbparams)
  cmd = cmd .. " >> " .. quotefile(consolefile)
  --if (gs.platform() == "win") then cmd = "cmd.exe /C " .. cmd end
  print("cmd=" .. cmd)
  local f = io.open(consolefile, "a")
  f:write("CMD=" .. cmd .. "\r\n")  
  f:close()
  gs.exec(cmd)
  -- collect output files
  gs.clearlistbox(lb)
  files = {}  
  lfiles = gs.files(outputdir)  
  -- reencode
  if gs.checkboxvalue(chencode) > 0 then        
    for k,v in pairs(lfiles) do 
      local pat = string.gsub(fc[2], '%-', '%%-')
      if ((string.match(v, pat .. "[_%d]*"..fc[3])) ~= nil) then      
        local fc = gs.splitfilename(v)
        local outfile = outputdir .. fc[2] .. "_reencoded" .. fc[3]                      
        idx = gs.comboboxindex(cbencode)  
        local cmd
        if (idx == 0) then        
          cmd = quotefile(TOOLSFOLDER .. "ffmpeg" .. EXEEXT) .. " -i " 
            .. quotefile(outputdir .. v) .. " -f mp4 -b 9000k -ab 128 -ar 44100 -y " .. quotefile(outfile)  
          cmd = cmd .. " " .. REDIR_STDERR_NULL
        else
          outfile = outputdir .. fc[2] .. "_reencoded" .. '.avi'                      
          cmd = quotefile(TOOLSFOLDER .. "mencoder" .. EXEEXT) .. " " 
            .. quotefile(outputdir .. v) .. " -ovc lavc -lavfopts format=mp4 -oac mp3lame -o " .. quotefile(outfile)  
          cmd = cmd .. " " .. REDIR_STDERR_NULL        
        end  
        print("cmd=" .. cmd)        
        local f = io.open(consolefile, "a")        
        f:write("CMD=" .. cmd .. "\r\n")  
        f:close()        
        gs.exec(cmd)        
      end
    end 
  end     
  lfiles = gs.files(outputdir)  
  for k,v in pairs(lfiles) do 
    local pat = string.gsub(fc[2], '%-', '%%-')
    if ((string.match(v, pat)) ~= nil) then
      gs.addlistbox(lb, v)
      table.insert(files, v)
    end
  end  
  gs.setlabel(labstatus, "Scan finished.")  
end

function stop()
  print("stop")  
  stopaction = true
end

function preview()
  print("preview")  
  if proc ~= nil then return end  
  local idx = gs.listboxindex(lb)
  if (idx == -1) then idx = 0 end
  gs.setlabel(labstatus, "Previewing - Please wait...")    
  local item = files[idx+1]
  idx = gs.comboboxindex(cbpreview)  
  local cmd 
  if (idx == 1) then
    cmd = "open -a 'QuickTime Player' "
  else
    cmd = quotefile(TOOLSFOLDER .. "mplayer" .. EXEEXT) .. " "       
  end    
  cmd = cmd .. quotefile(outputdir .. item)
  if (gs.platform() == "win") then
    cmd = cmd .. " 2> NUL"
  else
    cmd = cmd .. " 2> /dev/null"
  end    
  stopaction = false
  print("cmd=" .. cmd)
  proc = gs.openpipe(cmd, "r")  
end


-- create GUI
local w = 400
local h0 = 18
local h = 24
local h1 = 40
local h11 = 50
local h2 = 120
local pady = 2
local y = 10
local x = 20
win = gs.window{caption=APPNAME, rect={80,30,860,740} }
--gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Choose movie...", onclick="choosefile()" }


--gs.run()
--os.exit()

-- left pane
gs.label{parent=win, rect={x,y,w,h1}, caption="This program can repair damaged .mov/.mp4 files - Choose a movie to repair and press scan!" }
y = y + h1 + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Choose movie...", onclick="choosefile()" }
y = y + h1 + pady
gs.label{parent=win, rect={x,y,w,h1}, caption="Input movie: " }
y = y + h0
labfile = gs.label{parent=win, rect={x,y,w,h}, caption="<not yet selected>" }
y = y + h
gs.label{parent=win, rect={x,y,w,h1}, caption="Output folder: " }
y = y + h0
laboutput = gs.label{parent=win, rect={x,y,w,h}, caption="<not yet selected>" }
y = y + h
gs.label{parent=win, rect={x,y,w,h1}, caption="Reference movie: " }
y = y + h0
labreffile = gs.label{parent=win, rect={x,y,w,h}, caption="<not yet selected>" }
y = y + h
gs.label{parent=win, rect={x,y,w,h1}, caption="Status: " }
y = y + h0
labstatus = gs.label{parent=win, rect={x,y,w,h}, caption="Waiting for user to choose input movie." }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Scan", onclick="scan()" }

y = y + h1 + pady
gs.label{parent=win, rect={x,y,w,h}, caption="Repaired movie clips (blank if movie was not repairable):" }
y = y + h + pady
lb = gs.listbox{parent=win, rect={20,y,w,h2} }
y = y + h2 + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Preview", onclick="preview()" }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Stop", onclick="stop()" }
y = y + h1 + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Say thank you", onclick="saythankyou()" }
y = y + h1 + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Exit", onclick="gs.exit()" }
y = y + h1 + pady
gs.label{parent=win, rect={x,y,w,h11}, caption="NOTE: If the repaired movie does not play, choose a non-broken sample movie of the same camera type as reference and press again scan!" }
y = y + h11 + pady
gs.label{parent=win, rect={x,y,w,h1}, caption="Contact us if you think your movie should be repairable." }


-- right pane
x = 450
y = 10
gs.box{parent=win, rect={430,20,2,670}}
y = y + h1 + pady
gs.label{parent=win, rect={x,y,w,h}, caption="Tune your repair (optional):" }
y = y + h + pady
gs.label{parent=win, rect={x,y,w/2,h}, caption="Video codec:" }
cbvcodec = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h + pady
gs.label{parent=win, rect={x,y,w/2,h}, caption="Audio codec:" }
cbacodec = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h + pady
gs.label{parent=win, rect={x,y,w/2,h}, caption="Frames per second (FPS):" }
cbfps = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="Video image width:" }
cbwidth = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="Video image height:" }
cbheight = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="Audio sample rate:" }
cbsamplerate = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="Audio channels:" }
cbchannels = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h
gs.label{parent=win, rect={x,y,w/2,h}, caption="Audio sample size:" }
cbsamplesize = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h
-- gs.label{parent=win, rect={x,y,w/2,h}, caption="Minimum audio chunk size:" }
-- cbasmin = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
-- y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="App for preview:" }
cbpreview = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="Custom parameters:" }
cbparams = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h 
gs.label{parent=win, rect={x,y,w/2,h}, caption="App for reencoding:" }
cbencode = gs.combobox{parent=win,  rect={x+w/2,y,w/2,h} }
y = y + h
chnfd = gs.checkbox{parent=win, rect={x,y,w,h}, caption="Detect new movie clips" }
y = y + h
chpcm = gs.checkbox{parent=win, rect={x,y,w,h}, caption="Enable PCM detection" }
y = y + h
chaac = gs.checkbox{parent=win, rect={x,y,w,h}, caption="Enable AAC detection" }
y = y + h
chraw = gs.checkbox{parent=win, rect={x,y,w,h}, caption="Save raw video+audio streams to files" }
y = y + h 
chencode = gs.checkbox{parent=win, rect={x,y,w,h}, caption="Reencode repaired movie files (fixes encoding errors)" }
y = y + h1 + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Choose reference movie...", onclick="choosereffile()" }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Change output folder...", onclick="choosefolder()" }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Open repair log", onclick="openlogfile()" }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Clear repair log", onclick="clearlogfile()" }
y = y + h + pady
gs.button{parent=win,  rect={x+w*0.2,y,w*0.6,h}, caption="Create truncated movie", onclick="truncfile(4000000)" }


-- fill in values
gs.selectcheckbox(chnfd, 1)
--gs.selectcheckbox(chpcm, 1)
for k,v in pairs(lacodec) do gs.addcombobox(cbacodec, v) end
gs.selectcombobox(cbacodec, 0)
for k,v in pairs(lvcodec) do gs.addcombobox(cbvcodec, v) end
gs.selectcombobox(cbvcodec, 0)
for k,v in pairs(lfps) do gs.addcombobox(cbfps, v) end
gs.selectcombobox(cbfps, 0)
for k,v in pairs(lwidth) do gs.addcombobox(cbwidth, v) end
gs.selectcombobox(cbwidth, 0)
for k,v in pairs(lheight) do gs.addcombobox(cbheight, v) end
gs.selectcombobox(cbheight, 0)
for k,v in pairs(lsamplerate) do gs.addcombobox(cbsamplerate, v) end
gs.selectcombobox(cbsamplerate, 0)
for k,v in pairs(lchannels) do gs.addcombobox(cbchannels, v) end
gs.selectcombobox(cbchannels, 0)
for k,v in pairs(lsamplesize) do gs.addcombobox(cbsamplesize, v) end
gs.selectcombobox(cbsamplesize, 0)
for k,v in pairs(lpreview) do gs.addcombobox(cbpreview, v) end
gs.selectcombobox(cbpreview, 0)
for k,v in pairs(lencode) do gs.addcombobox(cbencode, v) end
gs.selectcombobox(cbencode, 0)
-- for k,v in pairs(lasmin) do gs.addcombobox(cbasmin, v) end
-- gs.selectcombobox(cbasmin, 0)

timer = gs.timer(30, "timercall()")

--print("blub")
-- proc = gs.openpipe("/Users/nero/Projects/fr/movdump/output/toolsdarwin/mplayer /Users/nero/Movies/repaired/kodak1.MOV 2> /dev/null", "r")
--proc = gs.openpipe("Z:/Users/nero/Projects/fr/movdump/output/toolswin/mplayer.exe /Users/nero/Movies/repaired/kodak1.MOV", "r")

--gs.exec("c:\\windows\\system32\\calc.exe")

print("go")

-- gs.messagebox{caption="blub", msg="msg"}

-- print("platform=" .. gs.platform())
-- print("scriptfolder=" .. SCRIPTFOLDER)

-- gs.sleep(3000)
gs.run()

-- os.exit()

