//===-- MachThread.h --------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  Created by Greg Clayton on 6/19/07.
//
//===----------------------------------------------------------------------===//

#ifndef __MachThread_h__
#define __MachThread_h__

#include <string>
#include <vector>

#include <libproc.h>
#include <mach/mach.h>
#include <pthread.h>
#include <sys/signal.h>

#include "PThreadCondition.h"
#include "PThreadMutex.h"
#include "MachException.h"
#include "DNBArch.h"
#include "DNBRegisterInfo.h"

#include "ThreadInfo.h"

class DNBBreakpoint;
class MachProcess;
class MachThreadList;

class MachThread
{
public:

                    MachThread (MachProcess *process, bool is_64_bit, uint64_t unique_thread_id = 0, thread_t mach_port_number = 0);
                    ~MachThread ();

    MachProcess *   Process() { return m_process; }
    const MachProcess *
                    Process() const { return m_process; }
    nub_process_t   ProcessID() const;
    void            Dump(uint32_t index);
    uint64_t        ThreadID() const { return m_unique_id; }
    thread_t        MachPortNumber() const { return m_mach_port_number; }
    thread_t        InferiorThreadID() const;

    uint32_t        SequenceID() const { return m_seq_id; }
    static bool     ThreadIDIsValid(uint64_t thread);       // The 64-bit system-wide unique thread identifier
    static bool     MachPortNumberIsValid(thread_t thread); // The mach port # for this thread in debugserver namespace
    void            Resume(bool others_stopped);
    void            Suspend();
    bool            SetSuspendCountBeforeResume(bool others_stopped);
    bool            RestoreSuspendCountAfterStop();

    bool            GetRegisterState(int flavor, bool force);
    bool            SetRegisterState(int flavor);
    uint64_t        GetPC(uint64_t failValue = INVALID_NUB_ADDRESS);    // Get program counter
    bool            SetPC(uint64_t value);                              // Set program counter
    uint64_t        GetSP(uint64_t failValue = INVALID_NUB_ADDRESS);    // Get stack pointer

    DNBBreakpoint * CurrentBreakpoint();
    uint32_t        EnableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
    uint32_t        EnableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task);
    bool            DisableHardwareBreakpoint (const DNBBreakpoint *breakpoint);
    bool            DisableHardwareWatchpoint (const DNBBreakpoint *watchpoint, bool also_set_on_task);
    uint32_t        NumSupportedHardwareWatchpoints () const;
    bool            RollbackTransForHWP();
    bool            FinishTransForHWP();

    nub_state_t     GetState();
    void            SetState(nub_state_t state);

    void            ThreadWillResume (const DNBThreadResumeAction *thread_action, bool others_stopped = false);
    bool            ShouldStop(bool &step_more);
    bool            IsStepping();
    bool            ThreadDidStop();
    bool            NotifyException(MachException::Data& exc);
    const MachException::Data& GetStopException() { return m_stop_exception; }

    nub_size_t      GetNumRegistersInSet(nub_size_t regSet) const;
    const char *    GetRegisterSetName(nub_size_t regSet) const;
    const DNBRegisterInfo *
                    GetRegisterInfo(nub_size_t regSet, nub_size_t regIndex) const;
    void            DumpRegisterState(nub_size_t regSet);
    const DNBRegisterSetInfo *
                    GetRegisterSetInfo(nub_size_t *num_reg_sets ) const;
    bool            GetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value );
    bool            SetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value );
    nub_size_t      GetRegisterContext (void *buf, nub_size_t buf_len);
    nub_size_t      SetRegisterContext (const void *buf, nub_size_t buf_len);
    uint32_t        SaveRegisterState ();
    bool            RestoreRegisterState (uint32_t save_id);

    void            NotifyBreakpointChanged (const DNBBreakpoint *bp)
                    {
                    }

    bool            IsUserReady();
    struct thread_basic_info *
                    GetBasicInfo ();
    const char *    GetBasicInfoAsString () const;
    const char *    GetName ();
    
    DNBArchProtocol* 
    GetArchProtocol()
    {
        return m_arch_ap.get();
    }

    ThreadInfo::QoS GetRequestedQoS (nub_addr_t tsd, uint64_t dti_qos_class_index);
    nub_addr_t      GetPThreadT();
    nub_addr_t      GetDispatchQueueT();
    nub_addr_t      GetTSDAddressForThread (uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);

    static uint64_t GetGloballyUniqueThreadIDForMachPortID (thread_t mach_port_id);

protected:
    static bool     GetBasicInfo(thread_t threadID, struct thread_basic_info *basic_info);

    bool
    GetIdentifierInfo ();

//    const char *
//    GetDispatchQueueName();
//
    MachProcess *                   m_process;      // The process that owns this thread
    uint64_t                        m_unique_id;    // The globally unique ID for this thread (nub_thread_t)
    thread_t                        m_mach_port_number;  // The mach port # for this thread in debugserver namesp.
    uint32_t                        m_seq_id;       // A Sequential ID that increments with each new thread
    nub_state_t                     m_state;        // The state of our process
    PThreadMutex                    m_state_mutex;  // Multithreaded protection for m_state
    struct thread_basic_info        m_basic_info;   // Basic information for a thread used to see if a thread is valid
    int32_t                         m_suspend_count; // The current suspend count > 0 means we have suspended m_suspendCount times,
                                                    //                           < 0 means we have resumed it m_suspendCount times.
    MachException::Data             m_stop_exception; // The best exception that describes why this thread is stopped
    std::unique_ptr<DNBArchProtocol> m_arch_ap;      // Arch specific information for register state and more
    const DNBRegisterSetInfo *      m_reg_sets;      // Register set information for this thread
    nub_size_t                      m_num_reg_sets;
    thread_identifier_info_data_t   m_ident_info;
    struct proc_threadinfo          m_proc_threadinfo;
    std::string                     m_dispatch_queue_name;
    bool                            m_is_64_bit;

    // qos_class_t _pthread_qos_class_decode(pthread_priority_t priority, int *, unsigned long *);
    unsigned int                    (*m_pthread_qos_class_decode) (unsigned long priority, int*, unsigned long *);

private:
    friend class MachThreadList;
};

typedef std::shared_ptr<MachThread> MachThreadSP;

#endif
