// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _BASICTYPES_INCLUDED_
#define _BASICTYPES_INCLUDED_

#include "debug.h"

//
// Precision qualifiers
//
enum TPrecision : unsigned char
{
	// These need to be kept sorted
	EbpUndefined,
	EbpLow,
	EbpMedium,
	EbpHigh
};

inline const char *getPrecisionString(TPrecision precision)
{
	switch(precision)
	{
	case EbpHigh:		return "highp";		break;
	case EbpMedium:		return "mediump";	break;
	case EbpLow:		return "lowp";		break;
	default:			return "mediump";   break;   // Safest fallback
	}
}

//
// Basic type.  Arrays, vectors, etc., are orthogonal to this.
//
enum TBasicType : unsigned char
{
	EbtVoid,
	EbtFloat,
	EbtInt,
	EbtUInt,
	EbtBool,
	EbtGVec4,              // non type: represents vec4, ivec4, and uvec4
	EbtGenType,            // non type: represents float, vec2, vec3, and vec4
	EbtGenIType,           // non type: represents int, ivec2, ivec3, and ivec4
	EbtGenUType,           // non type: represents uint, uvec2, uvec3, and uvec4
	EbtGenBType,           // non type: represents bool, bvec2, bvec3, and bvec4
	EbtVec,                // non type: represents vec2, vec3, and vec4
	EbtIVec,               // non type: represents ivec2, ivec3, and ivec4
	EbtUVec,               // non type: represents uvec2, uvec3, and uvec4
	EbtBVec,               // non type: represents bvec2, bvec3, and bvec4
	EbtGuardSamplerBegin,  // non type: see implementation of IsSampler()
	EbtSampler2D,
	EbtSampler3D,
	EbtSamplerCube,
	EbtSampler2DArray,
	EbtSamplerExternalOES,  // Only valid if OES_EGL_image_external exists.
	EbtISampler2D,
	EbtISampler3D,
	EbtISamplerCube,
	EbtISampler2DArray,
	EbtUSampler2D,
	EbtUSampler3D,
	EbtUSamplerCube,
	EbtUSampler2DArray,
	EbtSampler2DShadow,
	EbtSamplerCubeShadow,
	EbtSampler2DArrayShadow,
	EbtGuardSamplerEnd,    // non type: see implementation of IsSampler()
	EbtGSampler2D,         // non type: represents sampler2D, isampler2D, and usampler2D
	EbtGSampler3D,         // non type: represents sampler3D, isampler3D, and usampler3D
	EbtGSamplerCube,       // non type: represents samplerCube, isamplerCube, and usamplerCube
	EbtGSampler2DArray,    // non type: represents sampler2DArray, isampler2DArray, and usampler2DArray
	EbtStruct,
	EbtInterfaceBlock,
	EbtAddress,            // should be deprecated??
	EbtInvariant           // used as a type when qualifying a previously declared variable as being invariant
};

enum TLayoutMatrixPacking
{
	EmpUnspecified,
	EmpRowMajor,
	EmpColumnMajor
};

enum TLayoutBlockStorage
{
	EbsUnspecified,
	EbsShared,
	EbsPacked,
	EbsStd140
};

inline const char *getBasicString(TBasicType type)
{
	switch(type)
	{
	case EbtVoid:               return "void";
	case EbtFloat:              return "float";
	case EbtInt:                return "int";
	case EbtUInt:               return "uint";
	case EbtBool:               return "bool";
	case EbtSampler2D:          return "sampler2D";
	case EbtSamplerCube:        return "samplerCube";
	case EbtSamplerExternalOES: return "samplerExternalOES";
	case EbtSampler3D:			return "sampler3D";
	case EbtStruct:             return "structure";
	default: UNREACHABLE(type); return "unknown type";
	}
}

inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq)
{
	switch(mpq)
	{
	case EmpUnspecified:    return "mp_unspecified";
	case EmpRowMajor:       return "row_major";
	case EmpColumnMajor:    return "column_major";
	default: UNREACHABLE(mpq); return "unknown matrix packing";
	}
}

inline const char* getBlockStorageString(TLayoutBlockStorage bsq)
{
	switch(bsq)
	{
	case EbsUnspecified:    return "bs_unspecified";
	case EbsShared:         return "shared";
	case EbsPacked:         return "packed";
	case EbsStd140:         return "std140";
	default: UNREACHABLE(bsq); return "unknown block storage";
	}
}

inline bool IsSampler(TBasicType type)
{
	return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd;
}

inline bool IsIntegerSampler(TBasicType type)
{
	switch(type)
	{
	case EbtISampler2D:
	case EbtISampler3D:
	case EbtISamplerCube:
	case EbtISampler2DArray:
	case EbtUSampler2D:
	case EbtUSampler3D:
	case EbtUSamplerCube:
	case EbtUSampler2DArray:
		return true;
	case EbtSampler2D:
	case EbtSampler3D:
	case EbtSamplerCube:
	case EbtSamplerExternalOES:
	case EbtSampler2DArray:
	case EbtSampler2DShadow:
	case EbtSamplerCubeShadow:
	case EbtSampler2DArrayShadow:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsSampler2D(TBasicType type)
{
	switch(type)
	{
	case EbtSampler2D:
	case EbtISampler2D:
	case EbtUSampler2D:
	case EbtSampler2DArray:
	case EbtISampler2DArray:
	case EbtUSampler2DArray:
	case EbtSamplerExternalOES:
	case EbtSampler2DShadow:
	case EbtSampler2DArrayShadow:
		return true;
	case EbtSampler3D:
	case EbtISampler3D:
	case EbtUSampler3D:
	case EbtISamplerCube:
	case EbtUSamplerCube:
	case EbtSamplerCube:
	case EbtSamplerCubeShadow:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsSamplerCube(TBasicType type)
{
	switch(type)
	{
	case EbtSamplerCube:
	case EbtISamplerCube:
	case EbtUSamplerCube:
	case EbtSamplerCubeShadow:
		return true;
	case EbtSampler2D:
	case EbtSampler3D:
	case EbtSamplerExternalOES:
	case EbtSampler2DArray:
	case EbtISampler2D:
	case EbtISampler3D:
	case EbtISampler2DArray:
	case EbtUSampler2D:
	case EbtUSampler3D:
	case EbtUSampler2DArray:
	case EbtSampler2DShadow:
	case EbtSampler2DArrayShadow:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsSampler3D(TBasicType type)
{
	switch(type)
	{
	case EbtSampler3D:
	case EbtISampler3D:
	case EbtUSampler3D:
		return true;
	case EbtSampler2D:
	case EbtSamplerCube:
	case EbtSamplerExternalOES:
	case EbtSampler2DArray:
	case EbtISampler2D:
	case EbtISamplerCube:
	case EbtISampler2DArray:
	case EbtUSampler2D:
	case EbtUSamplerCube:
	case EbtUSampler2DArray:
	case EbtSampler2DShadow:
	case EbtSamplerCubeShadow:
	case EbtSampler2DArrayShadow:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsSamplerArray(TBasicType type)
{
	switch(type)
	{
	case EbtSampler2DArray:
	case EbtISampler2DArray:
	case EbtUSampler2DArray:
	case EbtSampler2DArrayShadow:
		return true;
	case EbtSampler2D:
	case EbtISampler2D:
	case EbtUSampler2D:
	case EbtSamplerExternalOES:
	case EbtSampler3D:
	case EbtISampler3D:
	case EbtUSampler3D:
	case EbtISamplerCube:
	case EbtUSamplerCube:
	case EbtSamplerCube:
	case EbtSampler2DShadow:
	case EbtSamplerCubeShadow:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsShadowSampler(TBasicType type)
{
	switch(type)
	{
	case EbtSampler2DShadow:
	case EbtSamplerCubeShadow:
	case EbtSampler2DArrayShadow:
		return true;
	case EbtISampler2D:
	case EbtISampler3D:
	case EbtISamplerCube:
	case EbtISampler2DArray:
	case EbtUSampler2D:
	case EbtUSampler3D:
	case EbtUSamplerCube:
	case EbtUSampler2DArray:
	case EbtSampler2D:
	case EbtSampler3D:
	case EbtSamplerCube:
	case EbtSamplerExternalOES:
	case EbtSampler2DArray:
		return false;
	default:
		assert(!IsSampler(type));
	}

	return false;
}

inline bool IsInteger(TBasicType type)
{
	return type == EbtInt || type == EbtUInt;
}

inline bool SupportsPrecision(TBasicType type)
{
	return type == EbtFloat || type == EbtInt || type == EbtUInt || IsSampler(type);
}

//
// Qualifiers and built-ins.  These are mainly used to see what can be read
// or written, and by the machine dependent translator to know which registers
// to allocate variables in.  Since built-ins tend to go to different registers
// than varying or uniform, it makes sense they are peers, not sub-classes.
//
enum TQualifier : unsigned char
{
	EvqTemporary,     // For temporaries (within a function), read/write
	EvqGlobal,        // For globals read/write
	EvqConstExpr,     // User defined constants
	EvqAttribute,     // Readonly
	EvqVaryingIn,     // readonly, fragment shaders only
	EvqVaryingOut,    // vertex shaders only  read/write
	EvqInvariantVaryingIn,     // readonly, fragment shaders only
	EvqInvariantVaryingOut,    // vertex shaders only  read/write
	EvqUniform,       // Readonly, vertex and fragment

	EvqVertexIn,      // Vertex shader input
	EvqFragmentOut,   // Fragment shader output
	EvqVertexOut,     // Vertex shader output
	EvqFragmentIn,    // Fragment shader input

	// pack/unpack input and output
	EvqInput,
	EvqOutput,

	// parameters
	EvqIn,
	EvqOut,
	EvqInOut,
	EvqConstReadOnly,

	// built-ins written by vertex shader
	EvqPosition,
	EvqPointSize,
	EvqInstanceID,

	// built-ins read by fragment shader
	EvqFragCoord,
	EvqFrontFacing,
	EvqPointCoord,

	// built-ins written by fragment shader
	EvqFragColor,
	EvqFragData,
	EvqFragDepth,

	// GLSL ES 3.0 vertex output and fragment input
	EvqSmooth,        // Incomplete qualifier, smooth is the default
	EvqFlat,          // Incomplete qualifier
	EvqSmoothOut = EvqSmooth,
	EvqFlatOut = EvqFlat,
	EvqCentroidOut,   // Implies smooth
	EvqSmoothIn,
	EvqFlatIn,
	EvqCentroidIn,    // Implies smooth

	// end of list
	EvqLast
};

struct TLayoutQualifier
{
	static TLayoutQualifier create()
	{
		TLayoutQualifier layoutQualifier;

		layoutQualifier.location = -1;
		layoutQualifier.matrixPacking = EmpUnspecified;
		layoutQualifier.blockStorage = EbsUnspecified;

		return layoutQualifier;
	}

	bool isEmpty() const
	{
		return location == -1 && matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified;
	}

	int location;
	TLayoutMatrixPacking matrixPacking;
	TLayoutBlockStorage blockStorage;
};

//
// This is just for debug print out, carried along with the definitions above.
//
inline const char *getQualifierString(TQualifier qualifier)
{
	switch(qualifier)
	{
	case EvqTemporary:      return "Temporary";      break;
	case EvqGlobal:         return "Global";         break;
	case EvqConstExpr:      return "const";          break;
	case EvqConstReadOnly:  return "const";          break;
	case EvqAttribute:      return "attribute";      break;
	case EvqVaryingIn:      return "varying";        break;
	case EvqVaryingOut:     return "varying";        break;
	case EvqInvariantVaryingIn: return "invariant varying";	break;
	case EvqInvariantVaryingOut:return "invariant varying";	break;
	case EvqUniform:        return "uniform";        break;
	case EvqVertexIn:       return "in";             break;
	case EvqFragmentOut:    return "out";            break;
	case EvqVertexOut:      return "out";            break;
	case EvqFragmentIn:     return "in";             break;
	case EvqIn:             return "in";             break;
	case EvqOut:            return "out";            break;
	case EvqInOut:          return "inout";          break;
	case EvqInput:          return "input";          break;
	case EvqOutput:         return "output";         break;
	case EvqPosition:       return "Position";       break;
	case EvqPointSize:      return "PointSize";      break;
	case EvqInstanceID:     return "InstanceID";     break;
	case EvqFragCoord:      return "FragCoord";      break;
	case EvqFrontFacing:    return "FrontFacing";    break;
	case EvqFragColor:      return "FragColor";      break;
	case EvqFragData:       return "FragData";       break;
	case EvqFragDepth:      return "FragDepth";      break;
	case EvqSmooth:         return "Smooth";         break;
	case EvqFlat:           return "Flat";           break;
	case EvqCentroidOut:    return "CentroidOut";    break;
	case EvqSmoothIn:       return "SmoothIn";       break;
	case EvqFlatIn:         return "FlatIn";         break;
	case EvqCentroidIn:     return "CentroidIn";     break;
	default: UNREACHABLE(qualifier); return "unknown qualifier";
	}
}

#endif // _BASICTYPES_INCLUDED_
