require 'qcmd/plaintext'

module Qcmd
  module Commands
    # All Commands
    #
    # *_RESPONSE lists are commands that expect responses
    #

    NO_MACHINE_RESPONSE = %w(connect)

    MACHINE_RESPONSE = %w(workspaces)

    WORKSPACE_RESPONSE = %w(
      cueLists selectedCues runningCues runningOrPausedCues thump
    )

    WORKSPACE_NO_RESPONSE = %w(
      go stop pause resume reset panic
    )

    ALL_WORKSPACE_COMMANDS = [WORKSPACE_RESPONSE + WORKSPACE_NO_RESPONSE]

    # commands that take no args and do not respond
    CUE_NO_RESPONSE = %w(
      start stop pause resume load preview reset panic
    )

    # commands that take args but do not respond
    CUE_ARG_NO_RESPONSE = %w(
      loadAt
    )

    # commands that always expect a response
    CUE_RESPONSE = %w(
      uniqueID hasFileTargets hasCueTargets allowsEditingDuration isLoaded
      isRunning isPaused isBroken preWaitElapsed actionElapsed
      postWaitElapsed percentPreWaitElapsed percentActionElapsed
      percentPostWaitElapsed type sliderLevels
      basics children
    )

    # commands that take args but expect a response if given without args
    NO_ARG_CUE_RESPONSE = %w(
      number name notes cueTargetNumber cueTargetId preWait duration
      postWait continueMode flagged armed colorName
      sliderLevel
    )

    # all cue commands that take arguments
    CUE_ARG = CUE_ARG_NO_RESPONSE + NO_ARG_CUE_RESPONSE

    ALL_CUE_COMMANDS = CUE_NO_RESPONSE +
                       CUE_ARG_NO_RESPONSE +
                       CUE_RESPONSE +
                       NO_ARG_CUE_RESPONSE

    class << self
      def no_machine_response_matcher
        @no_machine_response_matcher ||= %r[(#{NO_MACHINE_RESPONSE.join('|')})]
      end
      def no_machine_response_match command
        !!(no_machine_response_matcher =~ command)
      end

      def machine_response_matcher
        @machine_response_matcher ||= %r[(#{MACHINE_RESPONSE.join('|')})]
      end
      def machine_response_match command
        !!(machine_response_matcher =~ command)
      end

      def workspace_response_matcher
        @workspace_response_matcher ||= %r[(#{WORKSPACE_RESPONSE.join('|')})]
      end
      def workspace_response_match command
        !!(workspace_response_matcher =~ command)
      end

      def cue_response_matcher
        @cue_response_matcher ||= %r[(#{CUE_RESPONSE.join('|') })]
      end
      def cue_response_match command
        !!(cue_response_matcher =~ command)
      end

      def cue_no_arg_response_matcher
        @cue_no_arg_response_matcher ||= %r[(#{NO_ARG_CUE_RESPONSE.join('|') })]
      end
      def cue_no_arg_response_match command
        !!(cue_no_arg_response_matcher =~ command)
      end

      def expects_reply? osc_message
        address = osc_message.address

        Qcmd.debug "(check #{address} for reply expectation in connection state #{Qcmd.context.connection_state})"

        # debugger

        case Qcmd.context.connection_state
        when :none
          # shouldn't be dealing with OSC messages when unconnected to
          # machine or workspace
          response = no_machine_response_match(address)
        when :machine
          # could be workspace or machine command
          response = machine_response_match(address) ||
                     workspace_response_match(address)
        when :workspace
          if is_cue_command?(address)
            Qcmd.debug "- (checking cue command)"
            if osc_message.has_arguments?
              Qcmd.debug "- (with arguments)"
              response = cue_response_match address
            else
              Qcmd.debug "- (without arguments)"
              response = cue_no_arg_response_match(address) ||
                         cue_response_match(address)
            end
          else
            Qcmd.debug "- (checking workspace command)"
            response = workspace_response_match(address) ||
                       machine_response_match(address)
          end
        end

        response.tap {|value|
          msg = value ? "EXPECT REPLY" : "do not expect reply"
          Qcmd.debug "- (#{ msg })"
        }
      end

      def is_cue_command? address
        /cue/ =~ address && !(/Lists/ =~ address || /Cues/ =~ address)
      end

      def is_workspace_command? address
        /workspace/ =~ address && !(%r[cue/] =~ address || %r[cue_id/] =~ address)
      end
    end

    module Help
      class << self

        def print_all_commands
          Qcmd.print %[
#{Qcmd.centered_text(' Available Commands ', '-')}

exit

  close qcmd


connect MACHINE_NAME

  connect to the machine with name MACHINE_NAME


disconnect

  disconnect from the current machine and workspace


use WORKSPACE_NAME [PASSCODE]

  connect to the workspace with name WORKSPACE_NAME using passcode PASSCODE. A
  passcode is only required if the workspace has one enabled.


workspaces

  show a list of the available workspaces for the currently connected machine.


workspace COMMAND [VALUE]

  pass the given COMMAND to the connected workspace. The following commands will
  act on a workspace but will not return a value:

    #{Qcmd.wrapped_text(Qcmd::Commands::WORKSPACE_NO_RESPONSE.sort.join(', '), :indent => '    ').join("\n")}

  And these commands will not act on a workspace, but will return a value
  (usually a list of cues):

    #{Qcmd.wrapped_text((Qcmd::Commands::WORKSPACE_RESPONSE - ['connect']).sort.join(', '), :indent => '    ').join("\n")}

  * Pro Tip: once you are connected to a workspace, you can just use COMMAND
  and leave off "workspace" to quickly send the given COMMAND to the connected
  workspace.


cue NUMBER COMMAND [VALUE [ANOTHER_VALUE ...]]

  send a command to the cue with the given NUMBER.

  NUMBER can be a string or a number, depending on the command.

  COMMAND can be one of:

    #{Qcmd.wrapped_text(Qcmd::Commands::ALL_CUE_COMMANDS.sort.join(', '), :indent => '    ').join("\n")}

  Of those commands, only some accept a VALUE. The following commands, if given
  a value, will update the cue:

    #{Qcmd.wrapped_text(Qcmd::Commands::CUE_ARG.sort.join(', '), :indent => '    ').join("\n")}

  Some cues are Read-Only and will return information about a cue:

    #{Qcmd.wrapped_text(Qcmd::Commands::CUE_RESPONSE.sort.join(', '), :indent => '    ').join("\n")}

  Finally, some commands act on a cue, but don't take a VALUE and don't
  respond:

    #{Qcmd.wrapped_text(Qcmd::Commands::CUE_NO_RESPONSE.sort.join(', '), :indent => '    ').join("\n")}

]
        end
      end

    end
  end
end