/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/

#ifndef _MGAppearance_HH_
#define _MGAppearance_HH_

#include <iosfwd>
#include "mg/MGCL.h"
#include "mg/Group.h"
#include "mgGL/RenderAttr.h"

//
//Define MGAppearance Class.
class MGOfstream;
class MGIfstream;
class MGGLAttrib;
class MGLight;

/** @addtogroup DisplayHandling
 *  @{
 */

///A container class to hold MGGLAttrib objects.

///MGAppearance acts just like as std::auto_ptr.
///That is, MGAppearance holds newed object pointers of MGGLAttrib,
///and when copy constructor or assignment operator is invoked,
///the pointer ownership is transfered to the new MGAppearance object.
///A list of newed MGGLAttrib object pointer will be stored in parent MGGroup.
///No two same leaf type MGGLAttrib objects are included in this list.
class MG_DLL_DECLR MGAppearance: public MGAttrib{

public:

typedef MGGroup::iterator iterator;
typedef MGGroup::const_iterator const_iterator;

MGAppearance():m_no_display(false){;};

MGAppearance& operator=(const MGGel& gel2);
MGAppearance& operator=(const MGAppearance& gel2);

///comparison
bool operator<(const MGAppearance& gel2)const;
bool operator<(const MGGel& gel2)const;

////////Member Function////////

std::ostream& out(
	std::ostream& ostrm
)const;

MGAttrib* back(){return static_cast<MGAttrib*>(m_glattribs.back());};
const MGAttrib* back()const{return static_cast<const MGAttrib*>(m_glattribs.back());};
iterator begin(){return m_glattribs.begin();};
const_iterator begin()const{return m_glattribs.begin();};
void clear(){m_glattribs.clear();};
bool empty()const{return m_glattribs.empty();};
iterator end(){return m_glattribs.end();};
const_iterator end()const{return m_glattribs.end();};
MGAttrib* front(){return static_cast<MGAttrib*>(m_glattribs.front());};
const MGAttrib* front()const{return static_cast<const MGAttrib*>(m_glattribs.front());};
void pop_back(){m_glattribs.pop_back();};
void pop_front(){m_glattribs.pop_front();};
int size()const{return m_glattribs.size();};

///Add a light. light must be a newed object, and the ownership will be
///transfered to this object.
///Function's return value is the number of lights after added.
int add_light(MGLight* light);

///Test if this MGAppearance can be removed or not.
bool can_be_removed()const;

///Generate copied gel of this gel.
///Returned is a newed object. User must delete the object.
MGAppearance* clone()const;

///draw GLAttributes process.
void drawAttrib(
	mgVBO& vbo,///<The target graphic object.
	bool no_color=false	///<if true, color attribute will be neglected.
)const;

///Obtain display list name of the curren rendering context(MGOpenGLView).
///0(null) means the current MGOpenGLView=null or MGAppearance..
virtual mgVBO* dlist_name()const{return 0;};

///Judge if the display list for vmode is made or not.
bool displayList_is_made(MGCL::VIEWMODE vmode)const{return true;};

///Erase the specified attribute.
///Function's return value is the iterator after the erased data.
iterator erase(iterator i){return m_glattribs.erase(i);};

///Turn on the appropriate mask bit for this attribute. See glPushAttrib().
int get_draw_attrib_mask()const;
int get_render_attrib_mask()const;

/// Return This object's typeID
long identify_type() const{return MGAPPEARANCE_TID;};

///Get manifold dimension.
///MGGroup returns right one, MGGroup return 2, and others return -1.
int manifold_dimension() const{return -1;};

///Test this is no_display MGAppearance.
bool no_display()const{return m_no_display;};

///Release the attribute of specified type.
///Function's return value is the MGGLAttrib* that is released.
MGGLAttrib* release_attrib(long tid);

///render GLAttributes process.
void render(mgVBO& vbo)const;

///Search the same type MGGLAttrib leaf class object in this list.
///Function's return value is the iterator found.
///If not found, end() will be returned.
iterator search(const MGGLAttrib* atr);
iterator search_by_id(MGGEL_TID tid);
const_iterator search(const MGGLAttrib* atr)const;
const_iterator search_by_id(MGGEL_TID tid)const;

///Set the attribute in this list. attr must be a newed object, and the
///ownership will be transfered to this MGAppearance.
void set_attrib(MGGLAttrib* attr);
void set_attrib(MGGLAttribs& attrs);

///Set the attribute in this list. attr must be a newed object, and the
///ownership will be transfered to this MGAppearance.
///When the appearance held an attribute, the old one will be returned
///as the function's return value. Users must delete it.
MGGLAttrib* set_attrib_with_old(MGGLAttrib* attr);

void set_color(const MGColor& color);
void set_color(const float color[4]);
void set_color(float red, float green, float blue, float alpha=1.);

void set_display(){	m_no_display=false;};
void set_no_display(){	m_no_display=true;}

void set_light_disabled();
void set_light_enabled();

///Set the material. When rs=FRONT_AND_BACK and different material for the back side
///is used, set_back_material must be invoked after invoking set_material.
///Else the same material will be appllied for the both sides.
void set_material(
	MGRenderAttr::RENDERSIDE rs,
	const float ambient[3],
	const float diffuse[3],
	const float specular[3],
	const float emission[3],
	float shininess=0.,
	float transparency=0.
);

///Set the back side material. Invoking set_back_material means two sided material
///and setting different material to the back side.
///Before use of set_back_material, set_material must be invoked first.
///set_back_material will set two sided material.
void set_back_material(
	const float ambient[3],
	const float diffuse[3],
	const float specular[3],
	const float emission[3],
	float shininess=0.,
	float transparency=0.
);

///Line stipple属性をセットする。
///When factor=0 is input, line pattern is disabled. 実線となる
///When factor<0, the stipple attribute is undefined. This means the attribute
///is defined by the environment.
///When factor<=0, pattern is unnecessary.
void setLineStipple(short int factor,unsigned short pattern);

void setLineWidth(float width);

virtual std::string whoami()const{return "Appearance";};

protected:

	MGGroup m_glattribs;///<Attribute elements of this appearance.
	bool m_no_display;///<True if not to display, false if to display.

///メンバデータを読み出す関数
void ReadMembers(MGIfstream& buf);

///メンバデータを書き込む関数
void WriteMembers(MGOfstream& buf) const;

private:
iterator insert(iterator i, MGAttrib* atr){return m_glattribs.insert(i,atr);};
void push_back(MGAttrib* atr){m_glattribs.push_back(atr);};
void push_front(MGAttrib* atr){m_glattribs.push_front(atr);};

};

/** @} */ // end of DisplayHandling group
#endif //#ifndef _MGAppearance_HH_
