local Commands={["instances"]={}}

function Commands.new(pmtlist)

  local devices={["keyboard"]={},["gamepad"]={},["touchscreen"]={},["mouse"]={}}
  devices.istouch,devices.isgamepad,devices.ismouse,devices.iskeyboard=pmtlist.touchscreen or false,pmtlist.gamepad or true,pmtlist.mouse or false,pmtlist.keyboard or true
  devices.diagonals,devices.longkeys=pmtlist.diagonals or true,pmtlist.longkeys or true-- Lists desactivate everything
  devices.state,devices.timer=false,0
  
  devices.keypressed,devices.keyreleased,devices.keylist,devices.keylonglist="","",{},{}
  
  function devices.modifyperms(newpmtlist)
  
    istouch,isgamepad=newpmtlist.touchscreen or istouch,newpmtlist.gamepad or isgamepad
    ismouse,iskeyboard=newpmtlist.mouse or ismouse,newpmtlist.keyboard or iskeyboard
    diagonals,longkeys=newpmtlist.diagonals,newpmtlist.longkeys
  
  end
  
  function devices.mapping(dv,method,arg)
  
    -- Add a key
  
    if method=="add" then
    
      if dv=="keyboard" then
      
        --[[ example : key={syml={"w","up"},cmd="up",locked=false,type="movement"}
        cmd is the real key, syml is the symslink, 
        locked if bindable, only for first key in index of cmd, other are locked
        cmd and syml should be one of those keys : https://love2d.org/wiki/Scancode 
        The primordial type is "movement", good for diagonals --]]
        
        -- Detect for conflicts first (unchecked for bugs)
        
        for i in pairs(devices.keyboard) do
        
          if devices.keyboard[i].cmd==arg.cmd then
          
            return -1
          
          end
        
          for o in pairs(devices.keyboard[i].syml) do

            if devices.keyboard[i].syml[o]==arg.syml[o] then
            
              return -1
            
            end
          
          end
        
        end
      
        table.insert(devices.keyboard,arg)
        
        -- Call those keys with number index, e.g foo[x]
      
      elseif dv=="gamepad" then
      
      end
      
    -- Bind or swap a key, a table with the input and the target is awaited ({input="i",target="t"}) Still unchecked for bugs
      
    elseif method=="bind" then
    
      if dv=="keyboard" then
      
        local a,b
      
        for i in pairs(devices.keyboard) do
        
          if devices.keyboard[i].syml[1]==arg.input and devices.keyboard[i].locked then
          
            return -1
            
          elseif devices.keyboard[i].syml[1]==arg.target and devices.keyboard[i].locked then
          
            return -1
          
          end
          
          for o in pairs(devices.keyboard[i].syml) do
          
            -- You can't bind secondary keys
          
            if devices.keyboard[i].syml[o]==arg.input and o>1 then
            
              return -1
            
            elseif devices.keyboard[i].syml[o]==arg.input then
            
              a={i,o}
            
            elseif devices.keyboard[i].syml[o]==arg.target and o>1 then
            
              return -1
            
            elseif devices.keyboard[i].syml[o]==arg.target then
            
              b={i,o}
            
            end
        
          end
          
        end
          
        if a then

          if b then
            
            -- Swap keys
            
            devices.keyboard[a[1]].syml[a[2]],devices.keyboard[b[1]].syml[b[2]]=devices.keyboard[b[1]].syml[b[2]],devices.keyboard[a[1]].syml[a[2]]
            return 1
            
          end
            
          devices.keyboard[a[1]].syml[a[2]]=arg.target
          return 1
          
        end
          
        return -1
      
      elseif dv=="gamepad" then
      
      end
      
    -- Export mapping to a file, this time, arg is a string pointing to a file
    
    elseif method=="export" then
    
      if dv=="keyboard" then
    
        File.ectotoml(devices.keyboard,arg)
        
      elseif dv=="gamepad" then
      
      end
      
    -- Import the mapping of a file
    
    elseif method=="import" then
    
      if dv=="keyboard" then
         
        local a=File.dcfromtoml(arg)
        
        -- There can't be integers in toml. Therefore, they are all strings and needed to be converted
        
        for i in pairs(a) do
        
          for o in pairs(a[i]) do
          
            -- foo["x"] becomes foo[x]

            devices.keyboard[tonumber(i)]=a[i]
            
          end
          
        end
        
      elseif dv=="gamepad" then
      
      end
    
    end
  
  end
  
  -- Redirect to real key
  
  function devices.keylink(key)
  
    for i in pairs(devices.keyboard) do
    
      for o in pairs(devices.keyboard[i].syml) do

        if devices.keyboard[i].syml[o]==key then
        
          return devices.keyboard[i].cmd
        
        end
      
      end
    
    end
  
  end
  
  -- List all actives keys in lists
  
  function devices.list(listn) -- Has a complexity of O(n²)
  
    devices.keylist={}
    devices.keylonglist={}
    
    local dict={"up","right","down","left","up"}
    local a,b={}
  
    for i in pairs(devices.keyboard) do
    
      for o in pairs(devices.keyboard[i].syml) do
      
        if love.keyboard.isScancodeDown(devices.keyboard[i].syml[o]) then
        
          if listn=="timed" then
          
            table.insert(devices.keylonglist,devices.keyboard[i].cmd) 
          
          end
          
          if devices.diagonals then
          
            if devices.keyboard[i].type=="movement" then
            
              table.insert(a,devices.keyboard[i].cmd)

              if #a<2 then
              
                b=a[1].."-"..a[1]
               
              else
              
                b=a[1].."-"..a[2]
              
              end
            
              for n=1,4 do
              
                if b==dict[n].."-"..dict[n+1] or b==dict[n+1].."-"..dict[n] then
                
                  table.insert(devices.keylist,b)
                  table.remove(devices.keylist,1)
                  
                  -- up-right, right-down, down-left or left-up
                  
                end
              
              end
              
              -- If it failed, normal moves
              
              table.insert(devices.keylist,a[1])
            
            else
            
              table.insert(devices.keylist,devices.keyboard[i].cmd)
            
            end
          
          else 
        
            table.insert(devices.keylist,devices.keyboard[i].cmd)
            
          end
        
        end
      
      end
      
    end

  end
  
  function devices.detect(key,arg) -- O(n)
  
    if arg=="timed" then
    
      for i in pairs(devices.keylist) do
      
        if key==keylonglist[i] then
        
          return true
        
        end
      
      end
      
      return false
    
    else
    
      for i in pairs(devices.keylist) do
      
        if key==devices.keylist[i] then
        
          return true
        
        end
      
      end
      
      return false
    
    end
  
  end
  
  function devices.update(dt)
      
    -- To avoid workload for analyzing keyboard
    
    if devices.keypressed~="" or #devices.keylist~=0 then

      devices.list()
      
      if devices.state and devices.longkeys then
        
        devices.timer=devices.timer+7.5*dt
     
        if devices.timer>3 then
        
          devices.list("timed")
          devices.timer=0
            
        end
        
      end
        
    end
    
    devices.keypressed,devices.keyreleased="",""
  
  end
  
  table.insert(Commands.instances,devices)
  
  return devices
  
end

function Commands.update(dt)

  for i in pairs(Commands.instances) do
  
    if Commands.instances[i].iskeyboard then
  
      function love.keypressed(key,scancode)

        Commands.instances[i].keypressed=Commands.instances[i].keylink(scancode)
        Commands.instances[i].state=true
 
      end

      function love.keyreleased(key,scancode)
 
        Commands.instances[i].keyreleased=Commands.instances[i].keylink(scancode)
        Commands.instances[i].state=false
        Commands.instances[i].timer=0

      end
    
    end
  
    Commands.instances[i].update(dt)
  
  end

end

return Commands
