module Fog
  module Compute
    class Terremark
      class Server < Fog::Model
        module VAppStatus
          BEING_CREATED = "0"
          BEING_DEPLOYED = "1"
          POWERED_OFF = "2"
          POWERED_ON = "4"
        end

        identity :id

        attribute :name
        attribute :image
        attribute :vcpus
        attribute :memory
        attribute :sshkeyFingerPrint
        attribute :powerOn
        attribute :status
        attribute :OperatingSystem
        attribute :VirtualHardware
        attribute :IpAddress

        def reload
          merge_attributes(service.get_vapp(id).body)
        end

        def destroy
          case self.status
            when VAppStatus::BEING_CREATED, VAppStatus::BEING_DEPLOYED
              return false
            when VAppStatus::POWERED_ON
              service.power_off(self.id).body
              wait_for { off? }
          end
          #Failsafe .. Always check if we are ready
          wait_for { ready? }
          service.delete_vapp(self.id)
          true
        end

        def PublicIpAddress
          @PublicIpAddress ||=
              if internet_services.size > 0
                internet_services[0].PublicIpAddress["Name"]
              end
          @PublicIpAddress
        end

        def internet_services
          @internet_services ||= service.internetservices.all.select { |item| item.Name == self.name }
        end

        def delete_internet_services
          #Find the internet service

          while (inet_service = internet_services.pop) do

            nodes = service.nodeservices.all(inet_service.Id)
            #Delete all the associated nodes
            nodes.select { |item| item.destroy }
            #Clear out the services
            inet_service.destroy(delete_public_ip = !(internet_services.size > 0))
          end
          true
        end

        # { "0" => "Being created", "2" => "Powered Off", "4" => "Powered On"}
        def ready?
          status == VAppStatus::POWERED_OFF
        end

        def on?
          status == VAppStatus::POWERED_ON
        end

        def off?
          status == VAppStatus::POWERED_OFF
        end

        def power_on(_options = {})
          requires :id
          begin
            service.power_on(id)
          rescue Excon::Errors::InternalServerError => e
            # Frankly we shoudln't get here ...
            raise e unless e.to_s =~ /because it is already powered on/
          end
          true
        end

        def power_off
          requires :id
          begin
            service.power_off(id)
          rescue Excon::Errors::InternalServerError => e
            # Frankly we shoudln't get here ...
            raise e unless e.to_s =~ /because it is already powered off/
          end
          true
        end

        def shutdown
          requires :id
          begin
            service.power_shutdown(id)
          rescue Excon::Errors::InternalServerError => e
            # Frankly we shoudln't get here ...
            raise e unless e.to_s =~ /because it is already powered off/
          end
          true
        end

        def power_reset
          requires :id
          service.power_reset(id)
          true
        end

        def graceful_restart
          requires :id
          shutdown
          wait_for { off? }
          power_on
        end

        def create_internet_services(internet_spec)
          public_ip_info = nil
          internet_service_id = nil
          internet_spec.each do |proto, ports|
            for port in ports
              if not public_ip_info
                #Create the first internet service and allocate public IP
                inet_services = service.internetservices.create({
                                                                    "Name" => self.name,
                                                                    "Protocol" => proto,
                                                                    "Port" => port,
                                                                })
                internet_service_id = inet_services.Id
                public_ip_info = inet_services.PublicIpAddress

              else
                #create additional services to existing Public IP
                inet_services = service.internetservices.create({
                                                                    "public_ip_address_id" => public_ip_info["Id"],
                                                                    "Name" => self.name,
                                                                    "Protocol" => proto,
                                                                    "Port" => port,
                                                                }
                )
                internet_service_id = inet_services.Id
              end

              #Create the associate node service to the server
              service.nodeservices.create({"Name" => self.name,
                                           "IpAddress" => self.IpAddress,
                                           "Port" => port,
                                           "InternetServiceId" => internet_service_id
                                          })
            end
          end
          true
        end

        def save
          requires :name
          if powerOn
          end
          data = service.instantiate_vapp_template(
              server_name=name,
              vapp_template=image,
              options={
                "ssh_key_fingerprint" => sshkeyFingerPrint,
                "cpus" => vcpus,
                "memory" => memory
              })

          merge_attributes(data.body)
          wait_for { ready? }

          #Optional, incase further configuration required.
          if powerOn
            power_on
            wait_for { ready? }
          end

          true
        end

        private

        attr_writer :type, :size, :Links

        def href=(new_href)
          self.id = new_href.split("/").last.to_i
        end
      end
    end
  end
end
