require 'rubygems'
require 'rest_client'
require 'zip'
require 'zip/zipfilesystem'
require 'zip/zip'
require 'open-uri'

module Factor
  module Client
    class Client
      attr_accessor :host
    
      def initialize(host="http://factor.io")
        @host=host
      end
    
      def register(email,password)
      end
    
      def login(email, password)
      end
    
      def login_token(token)
        @token=token
      end
    
      def load_workflows(engine,&code)
        code.call("downloading workflow list") 
        workflows = rest_get("workflows")
        code.call("downloaded #{workflows.size} workflows") 
        workflows.each do |workflow_info|
          code.call("parsing '#{workflow_info['name']}'") 
          workflow_definition = JSON.parse(workflow_info['definition'])
          code.call("loading '#{workflow_info['name']}' into runtime") 
          workflow=Factor::Runtime::Workflow.new(workflow_definition)
          engine.load_workflow(workflow)
          code.call("loading complete for '#{workflow_info['name']}'") 
        end
        engine
      end
    
      def load_credentials(engine,&code)
        code.call("downloading credential list")
        credentials = rest_get("credentials")
        code.call("loading credentials")
        engine.load_credentials(credentials["value"])
      
        engine
      end
    
      def load_channels(engine,&code)
      
        # get list of channels
        code.call("downloading channels list")
        channels = rest_get("channels")
      
        #load each channel
        channels.each do |channel|
          code.call("downloading '#{channel['zip_url']}'")
          # URI of the zip file containing the module
          uri = URI.parse(channel['zip_url'])
        
          # temp file to store the zip for download
          #temp_file = Tempfile.new([uri.path.gsub(/\W+/,'-'), '.zip'], :encoding => 'ascii-8bit')
          temp_file = Tempfile.new([uri.to_s.gsub(/\W+/,'-'), '.zip'])
        
          # temp directory where zip will be decompressed
          unzip_dir=temp_file.path[0..-5]
        
          # download
          open(uri.to_s,"rb") do |bin|
            temp_file.print bin.read
          end
          temp_file.close
        
          code.call("unzipping '#{channel['name']}'")
          # unzip download zip into unzipped directory
          unzip(temp_file.path,unzip_dir)
        
          filename = unzip_dir + "/" + channel['name'].split(/(?=[A-Z])/).map{ |x| x.downcase }.join('_') + ".rb"
          
          code.call("loading '#{filename}' into engine")
          engine.load_channel(filename)
        end
      
      
        engine
      end
    
    
      def set_credential(key,value)
        # this is a PUT not POST because it is technically editing, not creating a new one
        rest_put("credentials",{:key=>key,:value=>value})
      end
      
      def get_credential(key="")
        rest_get("credentials",{:key=>key})
      end
      
      def remove_credential(key="")
        rest_delete("credentials",{:key=>key})
      end
    
    
    
    
      # workflows
      def add_workflow(key,definition)
        rest_post("workflows",{:name=>key,:definition=>definition})
      end
      
      def get_workflows()
        rest_get("workflows")
      end
      
      # def remove_workflow(name="")
      #   rest_delete("workflows",{:name=>name})
      # end
      
      
      # channels
      def add_channel(path,definition_file)
        file=zip(File.expand_path(path))
        definition = File.read(definition_file)
        rest_post("channels",{:zip=>file,:definition=>definition})
      end
      
      def get_channels()
        rest_get("channels")
      end
      
      # def remove_channel(name="")
      #   rest_delete("channels",{:name=>name})
      # end
      
      
    

    
    
      private
    
      def fix_github_zip_archive(file)
        Zip::ZipFile.open(file.path) do |zip|
          basedir = zip.entries.first.name
          zip.remove zip.entries.first
          zip.each do |entry|
            target = entry.name.sub(/^#{basedir}/,'')
            if entry.directory?
              zip.dir.mkdir(target)
            else
              zip.file.open(entry.name) do |istream|
                zip.file.open(target, 'w') do |ostream|
                  ostream.write istream.read
                end
              end
            end
            zip.remove entry
          end
        end
      end
    
      def unzip(zip, unzip_dir, remove_after = false)
        Zip::ZipFile.open(zip) do |zip_file|
          zip_file.each do |f|
            f_path=File.join(unzip_dir, f.name)
            FileUtils.mkdir_p(File.dirname(f_path))
            zip_file.extract(f, f_path) unless File.exist?(f_path)
          end
        end
        FileUtils.rm(zip) if remove_after
      end
      
      def zip(directory)
        path=directory.dup
        path.sub!(%r[/$],'')
        zip_path = File.join(path,"..",File.basename(path))+'.zip'
        FileUtils.rm zip_path, :force=>true
        Zip::ZipFile.open(zip_path, 'w') do |zip|
          Dir["#{path}/**/**"].reject{|f|f==zip_path}.each do |file|
            zip.add(file.sub(path+'/',''),file)
          end
        end
        File.open(zip_path,'r')
      end
    
      def rest_get(path,params={})
        params["auth_token"]=@token
        
        param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
        
        JSON.parse(RestClient.get("#{@host}/#{path}.json?#{param_string}"))
      end
      
      def rest_put(path,params={})
        params["auth_token"]=@token
        
        JSON.parse(RestClient.put("#{@host}/#{path}.json",params))
      end
      
      def rest_post(path,params={})
        #params["auth_token"]=@token
        
        JSON.parse(RestClient.post("#{@host}/#{path}.json?auth_token=#{@token}",params))
      end
      
      def rest_delete(path,params={})
        params["auth_token"]=@token
        
        param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
        
        JSON.parse(RestClient.delete("#{@host}/#{path}.json?#{param_string}"))
      end
    
    end
  end
end