require 'yaml'

module HaveAPI::Fs::Components
  # This file serves as an IPC between the file system and outer processes,
  # mainly executables from the file system itself.
  #
  # It does not behave like a regular file, it's more like a local socket.
  # The process opens the file, writes a message with a command and then reads
  # a message with a response.
  #
  # Messages are formatted in YAML and separated by {MSG_DELIMITER}.
  class RemoteControlFile < File
    MSG_DELIMITER = "\nMSG_OVER\n"

    class FileHandle
      attr_accessor :read_buf
      attr_accessor :write_buf

      def initialize
        @read_buf = ''
        @write_buf = ''
      end

      def read(offset, size)
        @read_buf[offset, size]
      end

      def write(offset, size, buf)
        @write_buf[offset, size] = buf
      end

      def complete?
        @write_buf.end_with?(MSG_DELIMITER)
      end

      def parse
        cmd = YAML.load(@write_buf[0..(-1 - MSG_DELIMITER.size)])
        @write_buf.clear
        cmd
      end
    end

    def writable?
      true
    end

    def size
      # The size limits the maximum amount of data that can be read from this file
      4096
    end

    def raw_open(path, mode, rfusefs = nil)
      FileHandle.new
    end

    def raw_read(path, offset, size, handle = nil)
      handle.read(offset, size)
    end

    def raw_write(path, offset, size, buf, handle = nil)
      handle.write(offset, size, buf)

      if handle.complete?
        cmd = handle.parse

        case cmd[:action]
        when :execute
          ret = HaveAPI::Fs::RemoteControl.execute(context, cmd[:path])

        else
          raise Errno::EIO, "unsupported action '#{cmd[:action]}'"
        end

        handle.read_buf = YAML.dump(ret)
      end

      size
    end

    def raw_sync(path, datasync, handle = nil)
      nil
    end

    def raw_truncate(path, offset, handle = nil)
      true
    end
    
    def raw_close(path, handle = nil)
      nil
    end
  end
end