#! /usr/bin/env lua

--[[ made by 0×FELIX ]]

local function show_help()
  io.write [[

lcrypt - v0.0.1-alpha

  a fast lua based text encrypter
  it converts text to byte and shifts byte using the password

  arguments: 
    -p <password> -> password for encryption
    -i <input>    -> input file 
    -o <output>   -> output file 

  all threee arguments are nessesary

]]
  os.exit(69)
end

local function parse_arg(data)
  local out = {}
  if #data > 7 and #data < 5 then show_help() end
  if data[1] ~= 'e' and data[1] ~= 'd' then show_help()
  else out['mode'] = data[1] end
  for i = 1, #data do
    if data[i] == '-h' then show_help()
    elseif data[i] == '-i' then out['input'] = data[i+1]
    elseif data[i] == '-o' then out['output'] = data[i+1]
    elseif data[i] == '-p' then out['password'] = data[i+1]
    end
  end
  return out
end

local function lcrypt(mode, data, pass)
  local rem, val, offset
  local out, pass_len = {}, #pass

  if mode == 'e' then offset = -1
  elseif mode == 'd' then offset = 1
  end

  for i = 1, #data do
    rem = i % pass_len + 1
    if rem == 2 then offset = -1 * offset end
    val = data[i] + (offset * pass[rem])
    if val > 255 then val = val - 255
    elseif val < 0 then val = val + 255 end
    out[i] = val
  end
  return out
end

local function split(data, delimiter)
  local index, out = 1, {}
  for i = 1, #data do
    if data:sub(i, i) == delimiter then
      table.insert(out, data:sub(index, i-1))
      index = i + 1
    end
  end
  table.insert(out, data:sub(index, #data))
  return table.unpack(out)
end

local byte = string.byte
local char = string.char

-- argument handling 
local opt = parse_arg(arg)

-- password handling 
local pass = opt['password']
local byte_pass = {}
for c in pass:gmatch '.' do byte_pass[#byte_pass + 1] = byte(c) end

-- input file handling 
local input_file = assert(io.open(opt['input'], 'r'))
local input_data = input_file :read('a')
local byte_input = {}
for c in input_data:gmatch('.') do byte_input[#byte_input + 1] = byte(c) end

-- output file handling
if not opt['output'] then opt['output'] = ( split( opt['input'], '.' ) ) .. '.crypt' end
local output_file = assert(io.open(opt['output'], 'w'))
local byte_output = lcrypt(opt['mode'], byte_input, byte_pass)

for i = 1, #byte_output do output_file :write(char(byte_output[i])) end