//===-- PlatformiOSSimulatorCoreSimulatorSupport.h ----------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_
#define liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_

// C Includes
// C++ Includes
#include <functional>
#include <string>
#include <ostream>
#include <vector>
// Other libraries and framework includes
#ifdef __APPLE__
#include <objc/objc.h>
#else
typedef void *id;
#endif
// Project includes
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Error.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Target/ProcessLaunchInfo.h"

#include "llvm/ADT/Optional.h"

// And now the actual magic
namespace CoreSimulatorSupport
{
    class Process
    {
    public:
        lldb::pid_t
        GetPID ()
        {
            return m_pid;
        }
        
        explicit operator bool ()
        {
            return m_pid != LLDB_INVALID_PROCESS_ID;
        }
        
        lldb_private::Error
        GetError ()
        {
            return m_error;
        }
        
    private:
        Process (lldb::pid_t p);
        
        Process(lldb_private::Error error);

        Process (lldb::pid_t p, lldb_private::Error error);
        
        lldb::pid_t m_pid;
        lldb_private::Error m_error;
        
        friend class Device;
    };
    
    class ModelIdentifier {
    public:
        ModelIdentifier (const std::string& mi);
        ModelIdentifier ();
        
        explicit operator bool () const
        {
            return !m_versions.empty();
        }
        
        size_t
        GetNumVersions () const
        {
            return m_versions.size();
        }
        
        unsigned int
        GetVersionAtIndex (size_t idx) const
        {
            return m_versions[idx];
        }
        
        std::string
        GetFamily () const
        {
            return m_family.c_str();
        }
        
    private:
        std::string m_family;
        std::vector<unsigned int> m_versions;
    };
    
    class DeviceType
    {
    public:
        enum class ProductFamilyID : int32_t
        {
            iPhone = 1,
            iPad = 2,
            appleTV = 3,
            appleWatch = 4
        };
        
        DeviceType ();
        
        DeviceType (id d);
        
        explicit operator bool ();
        
        std::string
        GetName ();

        lldb_private::ConstString
        GetIdentifier ();
        
        ModelIdentifier
        GetModelIdentifier ();
        
        lldb_private::ConstString
        GetProductFamily ();
        
        ProductFamilyID
        GetProductFamilyID ();
        
    private:
        id m_dev;
        llvm::Optional<ModelIdentifier> m_model_identifier;
    };
    
    class OSVersion {
    public:
        OSVersion (const std::string& ver,
                   const std::string& build);

        OSVersion ();
        
        explicit operator bool () const
        {
            return !m_versions.empty();
        }
        
        size_t
        GetNumVersions () const
        {
            return m_versions.size();
        }
        
        unsigned int
        GetVersionAtIndex (size_t idx) const
        {
            return m_versions[idx];
        }
        
        const char*
        GetBuild () const
        {
            return m_build.c_str();
        }
        
    private:
        std::vector<unsigned int> m_versions;
        std::string m_build;
    };
    
    class DeviceRuntime
    {
    public:
        DeviceRuntime ();
        
        DeviceRuntime (id d);
        
        explicit operator bool ();
        
        OSVersion
        GetVersion ();
        
        bool
        IsAvailable ();
        
    private:
        id m_dev;
        llvm::Optional<OSVersion> m_os_version;
    };
    
    class Device
    {
    private:
        typedef unsigned long int NSUInteger;

    public:
        enum class State : NSUInteger
        {
            Creating,
            Shutdown,
            Booting,
            Booted,
            ShuttingDown
        };
        
        Device ();

        Device (id d);
        
        explicit operator bool ();
        
        std::string
        GetName () const;

        DeviceType
        GetDeviceType ();
        
        DeviceRuntime
        GetDeviceRuntime ();
        
        State
        GetState ();
        
        bool
        Boot (lldb_private::Error &err);
        
        bool
        Shutdown (lldb_private::Error &err);
        
        std::string
        GetUDID () const;
        
        Process
        Spawn (lldb_private::ProcessLaunchInfo& launch_info);
        
    private:
        id m_dev;
        llvm::Optional<DeviceType> m_dev_type;
        llvm::Optional<DeviceRuntime> m_dev_runtime;
        
        friend class DeviceSet;
    };
    
    bool
    operator > (const OSVersion& lhs,
                const OSVersion& rhs);
    
    bool
    operator > (const ModelIdentifier& lhs,
                const ModelIdentifier& rhs);
    
    bool
    operator < (const OSVersion& lhs,
                const OSVersion& rhs);
    
    bool
    operator < (const ModelIdentifier& lhs,
                const ModelIdentifier& rhs);
    
    bool
    operator == (const OSVersion& lhs,
                 const OSVersion& rhs);
    
    bool
    operator == (const ModelIdentifier& lhs,
                 const ModelIdentifier& rhs);
    
    bool
    operator != (const OSVersion& lhs,
                 const OSVersion& rhs);
    
    bool
    operator != (const ModelIdentifier& lhs,
                 const ModelIdentifier& rhs);
    
    class DeviceSet
    {
    public:
        static DeviceSet
        GetAllDevices ();
        
        static DeviceSet
        GetAvailableDevices ();
        
        size_t
        GetNumDevices ();
        
        Device
        GetDeviceAtIndex (size_t idx);

        void
        ForEach (std::function<bool(const Device &)> f);

        DeviceSet
        GetDevicesIf (std::function<bool(Device)> f);
        
        DeviceSet
        GetDevices (DeviceType::ProductFamilyID dev_id);
        
        Device
        GetFanciest (DeviceType::ProductFamilyID dev_id);

    private:
        DeviceSet (id arr) : m_dev(arr)
        {
        }
        
        id m_dev;
    };
}

#endif  // liblldb_PlatformiOSSimulatorCoreSimulatorSupport_h_
