//#####################################################################
// Copyright 2023, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYREGHT.txt.
//#####################################################################
#ifndef __REG__
#define __REG__

#include <Core/Data_Structures/ELEMENT_ID.h>
#include <Core/Data_Structures/HASH_REDUCE.h>
#include <cassert>
#include <iosfwd>

namespace PhysBAM{

const int mem_mask=0xF;
const int mem_reg=0x0;
const int mem_const=0x1;
const int mem_in=0x2;
const int mem_out=0x3;
const int mem_label=0x4;
const int mem_special=0x5;
const int mem_xform_mask=0xC000000;
const int mem_neg_flag=0x4000000;
const int mem_inv_flag=0x8000000;
const int reg_invalid=0x0;
const int reg_dont_care=0x1;

PHYSBAM_DECLARE_ELEMENT_ID(MEM_REG,int,ELEMENT_ID_HELPER::for_loop|ELEMENT_ID_HELPER::logical|ELEMENT_ID_HELPER::add_T);
PHYSBAM_DECLARE_ELEMENT_ID(MEM_IN,int,ELEMENT_ID_HELPER::for_loop|ELEMENT_ID_HELPER::logical|ELEMENT_ID_HELPER::add_T);
PHYSBAM_DECLARE_ELEMENT_ID(MEM_OUT,int,ELEMENT_ID_HELPER::for_loop|ELEMENT_ID_HELPER::logical|ELEMENT_ID_HELPER::add_T);
PHYSBAM_DECLARE_ELEMENT_ID(MEM_CONST,int,ELEMENT_ID_HELPER::for_loop|ELEMENT_ID_HELPER::logical|ELEMENT_ID_HELPER::add_T);
PHYSBAM_DECLARE_ELEMENT_ID(MEM_LABEL,int,ELEMENT_ID_HELPER::for_loop|ELEMENT_ID_HELPER::logical|ELEMENT_ID_HELPER::add_T);

struct REG
{
private:
    union
    {
        struct{unsigned r:28,type:4;};
        int raw;
    };
public:
    REG():r(reg_invalid),type(mem_special){}
    REG(int var,int mem_type):r(var),type(mem_type){}
    REG(MEM_REG var):r(Value(var)),type(mem_reg){}
    REG(MEM_IN var):r(Value(var)),type(mem_in){}
    REG(MEM_OUT var):r(Value(var)),type(mem_out){}
    REG(MEM_CONST var):r(Value(var)),type(mem_const){}
    REG(MEM_LABEL var):r(Value(var)),type(mem_label){}
    bool Is_Reg() const {return type==mem_reg;}
    bool Is_In() const {return type==mem_in;}
    bool Is_Out() const {return type==mem_out;}
    bool Is_Const() const {return type==mem_const;}
    bool Is_Label() const {return type==mem_label;}
    bool Is_Invalid() const {return type==mem_special && r==reg_invalid;}
    bool Is_Dont_Care() const {return type==mem_special && r==reg_dont_care;}
    bool Is_Value() const {return type<=mem_out;}
    MEM_REG Reg() const {assert(Is_Reg());return MEM_REG(r);}
    MEM_IN In() const {assert(Is_In());return MEM_IN(r);}
    MEM_OUT Out() const {assert(Is_Out());return MEM_OUT(r);}
    MEM_CONST Const() const {assert(Is_Const());return MEM_CONST(r);}
    MEM_LABEL Label() const {assert(Is_Label());return MEM_LABEL(r);}
    std::pair<int,int> Parts() const {return {type,r};}
    auto operator<=>(const REG& a) const{return raw<=>a.raw;}
    bool operator==(const REG& a) const{return raw==a.raw;}
    static REG Dont_Care(){return {reg_dont_care,mem_special};}
    int Get_Raw() const{return raw;}
};

struct REG_AUG
{
    REG reg{};
    bool neg=false;
    bool inv=false;
};

template<> struct HASH_REDUCE<REG>{static int H(const REG key){return HASH_REDUCE<int>::H(key.Get_Raw());}};

std::ostream& operator<<(std::ostream& o,const REG& r);
std::ostream& operator<<(std::ostream& o,const REG_AUG& r);

}
#endif
