--[[
  Copyright 2016 ARATA Mizuki

  This file is part of ClutTeX.

  ClutTeX is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  ClutTeX is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with ClutTeX.  If not, see <http://www.gnu.org/licenses/>.
]]

-- pathutil module

local assert = assert
local select = select
local string = string
local string_find = string.find
local string_sub = string.sub
local string_match = string.match
local string_gsub = string.gsub
local filesys = require "lfs"

local function basename(path)
  local i = 0
  while true do
    local j = string_find(path, "[\\/]", i + 1)
    if j == nil then
      return string_sub(path, i + 1)
    elseif j == #path then
      return string_sub(path, i + 1, -2)
    end
    i = j
  end
end

-- TEST CODE
assert(basename("/path/to/file") == "file")
assert(basename("/path/to/directory/") == "directory")
assert(basename([[c:\path\to/directory\]]) == "directory")
assert(basename("/file") == "file")
assert(basename("file") == "file")
-- END TEST CODE

local function dirname(path)
  local i = 0
  while true do
    local j = string_find(path, "[\\/]", i + 1)
    if j == nil then
      if i == 0 then
        -- No directory portion
        return "."
      elseif i == 1 then
        -- Root
        return string_sub(path, 1, 1)
      else
        -- Directory portion without trailing slash
        return string_sub(path, 1, i - 1)
      end
    end
    i = j
  end
end

-- TEST CODE
assert(dirname("/path/to/file") == "/path/to")
assert(dirname("/path/to/directory/") == "/path/to/directory")
assert(dirname([[c:/path\to/file]]) == [[c:/path\to]])
assert(dirname("/file") == "/")
assert(dirname("file") == ".")
-- END TEST CODE

local function parentdir(path)
  local i = 0
  while true do
    local j = string_find(path, "[\\/]", i + 1)
    if j == nil then
      if i == 0 then
        -- No directory portion
        return "."
      elseif i == 1 then
        -- Root
        return string_sub(path, 1, 1)
      else
        -- Directory portion without trailing slash
        return string_sub(path, 1, i - 1)
      end
    elseif j == #path then
      -- Directory portion without trailing slash
      return string_sub(path, 1, i - 1)
    end
    i = j
  end
end

-- TEST CODE
assert(parentdir("/path/to/file") == "/path/to")
assert(parentdir("/path/to/directory/") == "/path/to")
assert(parentdir("/file") == "/")
assert(parentdir("file") == ".")
-- END TEST CODE

local function trimext(path)
  return (string_gsub(path, "%.[^\\/%.]*$", ""))
end

-- TEST CODE
assert(trimext("/path/to/file.ext") == "/path/to/file")
assert(trimext("/path/t.o/file") == "/path/t.o/file")
assert(trimext([[c:/path/t.o\file]]) == [[c:/path/t.o\file]])
assert(trimext("file.ext") == "file")
assert(trimext("file.e.xt") == "file.e")
assert(trimext("file.ext.") == "file.ext")
assert(trimext("file") == "file")
-- END TEST CODE

local function ext(path)
  return string_match(path, "%.([^\\/%.]*)$") or ""
end

-- TEST CODE
assert(ext("/path/to/file.ext") == "ext")
assert(ext("/path/t.o/file") == "")
assert(ext([[c:/path/t.o\file]]) == "")
assert(ext("file.ext") == "ext")
assert(ext("file.e.xt") == "xt")
assert(ext("file.ext.") == "")
assert(ext("file") == "")
-- END TEST CODE

local function replaceext(path, newext)
  local newpath, n = string_gsub(path, "%.([^\\/%.]*)$", function() return "." .. newext end)
  if n == 0 then
    return newpath .. "." .. newext
  else
    return newpath
  end
end

-- TEST CODE
assert(replaceext("/path/to/file.ext", "tor") == "/path/to/file.tor")
assert(replaceext("/path/t.o/file", "tor") == "/path/t.o/file.tor")
assert(replaceext([[c:/path/t.o\file]], "tor") == [[c:/path/t.o\file.tor]])
assert(replaceext("file.ext", "tor") == "file.tor")
assert(replaceext("file.e.xt", "tor") == "file.e.tor")
assert(replaceext("file.ext.", "tor") == "file.ext.tor")
assert(replaceext("file", "tor") == "file.tor")
-- END TEST CODE

local function joinpath2(x, y)
  local xd = x
  local last = string_sub(x, -1)
  if last ~= "/" and last ~= "\\" then
    xd = x .. "\\"
  end
  if y == "." then
    return xd
  elseif y == ".." then
    return dirname(x)
  else
    if string_match(y, "^%.[\\/]") then
      return xd .. string_sub(y, 3)
    else
      return xd .. y
    end
  end
end

local function joinpath(...)
  local n = select("#", ...)
  if n == 2 then
    return joinpath2(...)
  elseif n == 0 then
    return "."
  elseif n == 1 then
    return ...
  else
    return joinpath(joinpath2(...), select(3, ...))
  end
end

-- TEST CODE
assert(joinpath("/path/", "to", "somewhere") == [[/path/to\somewhere]])
assert(joinpath("/path/", "to", "somewhere", "..") == [[/path/to]])
assert(joinpath("/path/", "to", "somewhere", "..", "elsewhere") == [[/path/to\elsewhere]])
assert(joinpath("/path/", "to", "./somewhere.txt") == "/path/to/somewhere.txt")
-- END TEST CODE

-- https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
local function isabspath(path)
  local init = string_sub(path, 1, 1)
  return init == "\\" or init == "/" or string_match(path, "^%a:[/\\]")
end

local function abspath(path, cwd)
  if isabspath(path) then
    -- absolute path
    return path
  else
    -- TODO: relative path with a drive letter is not supported
    cwd = cwd or filesys.currentdir()
    return joinpath2(cwd, path)
  end
end

return {
  basename = basename,
  dirname = dirname,
  parentdir = parentdir,
  trimext = trimext,
  ext = ext,
  replaceext = replaceext,
  join = joinpath,
  abspath = abspath,
}