/////////////////////////////////////////////////////////////////////////////
// Name:        MenuObject.cpp
// Purpose:     The class to store a DVD Menu Object
// Author:      Alex Thuering
// Created:	04.11.2006
// RCS-ID:      $Id: MenuObject.cpp,v 1.7 2008/03/02 18:56:21 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "Menu.h"

#include <wxVillaLib/utils.h>
#include <wxSVG/svg.h>
#include <wxSVGXML/svgxmlhelpr.h>
#include <wx/mstream.h>
#include <wx/filename.h>

#define BUTTONS_DIR wxFindDataDirectory(_T("buttons"))
#define OBJECTS_DIR wxFindDataDirectory(_T("objects"))

///////////////////////////// MenuAction /////////////////////////////////////

wxString MenuAction::GetAction()
{
  wxString action = m_action;
  if (!action.length())
  {
	int tsi = GetActionTsi();
	bool menu = GetActionPgci()%2 == 0;
	int pgci = GetActionPgci()/2;
	action = _T("jump ");
	if (tsi == -1)
	  action += _T("vmgm ");
	else if (tsi >= 0)
	  action += wxString::Format(_T("titleset %d "), tsi+1);
	if (menu)
	  action += _T("menu");
	else
	  action += _T("title");
	if (!menu || pgci>0 || tsi == -2)
	  action += wxString::Format(_T(" %d"), pgci+1);
	if (GetActionChapter() > 0)
	  action += wxString::Format(_T(" chapter %d"), GetActionChapter()+1);
	action += _T(";");
  }
  return action;
}

wxSvgXmlNode* MenuAction::GetXML(DVDFileType type, bool button) {
    wxSvgXmlNode* rootNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE,
        type == DVDAUTHOR_XML ? wxT("button") : wxT("action"));
    switch (type) {
        case DVDSTYLER_XML:
            if (!button)
                rootNode->AddProperty(_T("id"), GetId());
            if (!GetCustomAction().length()) {
                if (GetActionTsi()>-2)
                   rootNode->AddProperty(_T("tsi"), wxString::Format(_T("%d"), GetActionTsi()));
                rootNode->AddProperty(_T("pgci"), wxString::Format(_T("%d"), GetActionPgci()));
                rootNode->AddProperty(_T("chapter"), wxString::Format(_T("%d"), GetActionChapter()));
            } else
                rootNode->AddChild(new wxSvgXmlNode(wxSVGXML_TEXT_NODE, wxEmptyString, GetCustomAction()));
            break;
        case SPUMUX_XML:
            rootNode->AddProperty(_T("name"), GetId());
            break;
        case DVDAUTHOR_XML:
            rootNode->AddProperty(_T("name"), GetId());
            rootNode->AddChild(new wxSvgXmlNode(wxSVGXML_TEXT_NODE, wxEmptyString, GetAction()));
            break;
    	default:
    	  break;
    }
    return rootNode;
}

bool MenuAction::PutXML(wxSvgXmlNode* node, bool button) {
    if (!button)
        node->GetPropVal(_T("id"), &m_id);
    long lval;
    wxString val;
    if (node->GetPropVal(_T("tsi"), &val) && val.ToLong(&lval))
        SetActionTsi(lval);
    if (node->GetPropVal(_T("pgci"), &val) && val.ToLong(&lval))
        SetActionPgci(lval);
    if (node->GetPropVal(_T("chapter"), &val) && val.ToLong(&lval))
        SetActionChapter(lval);
    if (node->GetChildren()
          && (node->GetChildren()->GetType() == wxSVGXML_TEXT_NODE
           || node->GetChildren()->GetType() == wxSVGXML_CDATA_SECTION_NODE))
        SetCustomAction(node->GetChildren()->GetContent().Strip(wxString::both));
    return true;
}


///////////////////////////// MenuObject /////////////////////////////////////

MenuObject::MenuObject(Menu* menu, wxString fileName, int x, int y, wxString param)
{
  m_svg = menu->GetSVG();
  m_use = NULL;
  m_symbol = NULL;
  
  m_fileName = fileName;
  
  m_button = false;
  m_actionTsi = -2; // current titleset
  m_actionPgci = 1;
  m_actionChapter = 0;
  m_playAll = false;
  m_audio = -1;
  m_subtitle = -1;
  
  m_defaultSize = true;
  m_defaultSizeWidth = m_defaultSizeHeight = 0;
  m_minSizeWidth = m_minSizeHeight = 0;
  
  if (fileName.length())
    Init(fileName, x, y, param);
}

MenuObject::~MenuObject()
{
  WX_CLEAR_ARRAY(m_params)
  if (m_use)
    m_use->GetParent()->RemoveChild(m_use);
  if (m_symbol)
    m_symbol->GetParent()->RemoveChild(m_symbol);
}

bool MenuObject::Init(wxString fileName, int x, int y, wxString param)
{
  m_fileName = fileName;
  wxSvgXmlDocument xml;
  xml.Load(fileName);
  if (!xml.GetRoot())
    return false;
  wxSvgXmlNode* root = xml.GetRoot();
  
  m_button = root->GetName() == wxT("button");
  m_title = XmlReadValue(root, wxT("title"));
  
  if (m_id.length()) // load button
  {
    m_symbol = (wxSVGSVGElement*) m_svg->GetElementById(wxT("s_") + m_id);
    m_use = (wxSVGUseElement*) m_svg->GetElementById(m_id);
    
    // fix old format (1.5b1 - 1.5b3)
    if (m_symbol && m_symbol->GetDtd() == wxSVG_SYMBOL_ELEMENT)
    {
      // replace symbol element with svg element
      wxSVGSymbolElement* symbol = (wxSVGSymbolElement*) m_symbol;
      m_symbol = new wxSVGSVGElement();
      m_symbol->SetId(symbol->GetId());
      m_symbol->SetViewBox(symbol->GetViewBox());
      m_symbol->SetPreserveAspectRatio(symbol->GetPreserveAspectRatio());
      wxSvgXmlElement* child = symbol->GetChildren();
      while (child)
      {
        m_symbol->AddChild(child->CloneNode());
        child = child->GetNext();
      }
      wxSvgXmlElement* parent = symbol->GetParent();
      parent->RemoveChild(symbol);
      parent->AddChild(m_symbol);
      // remove id prefix
      child = m_symbol->GetChildren();
      while (child)
      {
        wxSVGElement& elem = *(wxSVGElement*) child;
        if (elem.GetId().length())
            elem.SetId(elem.GetId().AfterFirst(wxT('_')).AfterFirst(wxT('_')));
        child = child->GetNext();
      }
    }
  }
  else // create new button
    m_id = GenerateId(m_button ? wxT("button") : wxT("obj"));
  
  wxSvgXmlNode* svgNode = XmlFindNode(root, wxT("svg"));
  if (svgNode)
  {
    wxSVGDocument svg;
    LoadSVG(svg, svgNode);
    wxSVGSVGElement* root = svg.GetRootElement();
    if (root)
    {
      m_defaultSizeWidth = root->GetWidth().GetBaseVal();
      m_defaultSizeHeight = root->GetHeight().GetBaseVal();
      if (!m_symbol)
        AddSymol(m_id, root);
      if (!m_use)
        AddUse(m_id, x, y, m_defaultSizeWidth, m_defaultSizeHeight);
    }
  }
  
  wxSvgXmlNode* paramsNode = XmlFindNode(root, wxT("parameters"));
  if (paramsNode)
  {
    wxSvgXmlNode* child = paramsNode->GetChildren();
    while (child)
    {
      if (child->GetType() == wxSVGXML_ELEMENT_NODE &&
          child->GetName() == wxT("parameter"))
      {
        MenuObjectParam* param = new MenuObjectParam;
        param->title = XmlReadValue(child, wxT("title"));
        param->name = XmlReadValue(child, wxT("name"));
        param->type = XmlReadValue(child, wxT("type"));
        param->element = XmlReadValue(child, wxT("element"));
        param->attribute = XmlReadValue(child, wxT("attribute"));
        param->changeable =
          XmlFindNode(child, wxT("changeable")) != NULL &&
          param->type == wxT("colour");
        m_params.Add(param);
        if (param->changeable)
        {
          param->changeable = false;
          param->normalColour = GetParamColour(param->name);
          wxCSSStyleDeclaration style;
          style.SetProperty(wxCSS_PROPERTY_STROKE,
            XmlReadValue(child, wxT("default-value/highlighted")));
          param->highlightedColour = style.GetStroke().GetRGBColor();
          style.SetProperty(wxCSS_PROPERTY_STROKE,
            XmlReadValue(child, wxT("default-value/selected")));
          param->selectedColour = style.GetStroke().GetRGBColor();
          param->changeable = true;
        }
      }
      child = child->GetNext();
    }
  }
  
  m_initParameter = XmlReadValue(root, wxT("init-parameter"));
  if (m_initParameter.length() && param.length())
    SetParam(m_initParameter, param);
  
  wxSvgXmlNode* defaultSizeNode = XmlFindNode(root, wxT("default-size"));
  if (defaultSizeNode)
  {
    m_defaultSizeElement = XmlReadValue(defaultSizeNode, wxT("element"));
    long val;
    if (XmlReadValue(defaultSizeNode, wxT("width")).ToLong(&val))
      m_defaultSizeWidth = val;
    if (XmlReadValue(defaultSizeNode, wxT("height")).ToLong(&val))
      m_defaultSizeHeight = val;
  }
  
  wxSvgXmlNode* minSizeNode = XmlFindNode(root, wxT("min-size"));
  if (minSizeNode)
  {
    m_minSizeElement = XmlReadValue(minSizeNode, wxT("element"));
    long val;
    if (XmlReadValue(minSizeNode, wxT("width")).ToLong(&val))
      m_minSizeWidth = val;
    if (XmlReadValue(minSizeNode, wxT("height")).ToLong(&val))
      m_minSizeHeight = val;
  }
    
  UpdateSize();
  
  return true;
}

bool MenuObject::LoadSVG(wxSVGDocument& svg, wxSvgXmlNode* node)
{
  bool res;
  wxSvgXmlDocument xml;
  xml.SetRoot(node->CloneNode());
  wxMemoryOutputStream output;
  xml.Save(output);
#if wxCHECK_VERSION(2,6,0)
  wxMemoryInputStream input(output);
  res = svg.Load(input);
#else
  char* data = new char[output.GetSize()];
  output.CopyTo(data, output.GetSize());
  wxMemoryInputStream input(data, output.GetSize());
  res = svg.Load(input);
  delete[] data;
#endif
  return res;
}

wxString MenuObject::GenerateId(wxString prefix)
{
  int i = 1;
  while(1)
  {
    wxString id = prefix + wxString::Format(wxT("%02d"), i);
    if (m_svg->GetElementById(id) == NULL)
      return id;
    i++;
  }
  return wxT("");
}

MenuObjectParam* MenuObject::GetObjectParam(wxString name)
{
  for (int i=0; i<(int)m_params.Count(); i++)
  {
    if (m_params[i]->name == name)
      return m_params[i];
  }
  return NULL;
}

MenuObjectParam* MenuObject::GetInitParam()
{
  if (!m_initParameter.length())
    return NULL;
  return GetObjectParam(m_initParameter);
}

wxSVGSVGElement* MenuObject::AddSymol(wxString id, wxSVGElement* content)
{
  m_symbol = new wxSVGSVGElement;
  m_symbol->SetId(wxT("s_") + id);
  m_svg->GetElementById(wxT("defs"))->AppendChild(m_symbol);
  if (content->GetDtd() == wxSVG_SVG_ELEMENT)
  {
    m_symbol->SetViewBox(((wxSVGSVGElement*) content)->GetViewBox());
    m_symbol->SetPreserveAspectRatio(((wxSVGSVGElement*) content)->GetPreserveAspectRatio());
  }
  else if (content->GetDtd() == wxSVG_SYMBOL_ELEMENT)
  {
    m_symbol->SetViewBox(((wxSVGSymbolElement*) content)->GetViewBox());
    m_symbol->SetPreserveAspectRatio(((wxSVGSymbolElement*) content)->GetPreserveAspectRatio());
  }
  wxSvgXmlElement* child = content->GetChildren();
  while (child)
  {
    m_symbol->AppendChild(((wxSVGSVGElement*)child)->CloneNode());
    child = child->GetNext();
  }
  return m_symbol;
}

void MenuObject::AddUse(wxString id, int x, int y, int width, int height)
{
  m_use = new wxSVGUseElement;
  m_use->SetId(id);
  m_use->SetHref(wxT("#s_") + id);
  m_use->SetX(x);
  m_use->SetY(y);
  m_use->SetWidth(width);
  m_use->SetHeight(height);
  if (IsButton())
  	m_svg->GetElementById(wxT("buttons"))->AppendChild(m_use);
  else
  	m_svg->GetElementById(wxT("objects"))->AppendChild(m_use);
}

int MenuObject::GetX() { return m_use->GetX().GetBaseVal(); }
void MenuObject::SetX(int value) { m_use->SetX(value); }

int MenuObject::GetY() { return m_use->GetY().GetBaseVal(); }
void MenuObject::SetY(int value) { m_use->SetY(value); }

int MenuObject::GetWidth() { return m_use->GetWidth().GetBaseVal(); }
void MenuObject::SetWidth(int value) { m_use->SetWidth(value); }

int MenuObject::GetHeight() { return m_use->GetHeight().GetBaseVal(); }
void MenuObject::SetHeight(int value) { m_use->SetHeight(value); }

void MenuObject::FixSize(int& width, int& height)
{
  if (m_defaultSize)
  {
    width = m_defaultSizeWidth;
    height = m_defaultSizeHeight;
    if (m_defaultSizeElement.length())
    {
      wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(m_defaultSizeElement);
      if (elem)
      {
        if (elem->GetDtd() == wxSVG_IMAGE_ELEMENT)
        {
          width += ((wxSVGImageElement*)elem)->GetDefaultWidth();
          height += ((wxSVGImageElement*)elem)->GetDefaultHeight();
        }
        else
        {
          wxSVGRect bbox = wxSVGLocatable::GetElementResultBBox(elem);
          width += (int)bbox.GetWidth();
          height += (int)bbox.GetHeight();
        }
      }
    }
  }
  else
  {
    int w = m_minSizeWidth;
    int h = m_minSizeHeight;
    if (m_minSizeElement.length())
    {
      wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(m_minSizeElement);
      if (elem)
      {
        wxSVGRect bbox = wxSVGLocatable::GetElementResultBBox(elem);
        w += (int)bbox.GetWidth();
        h += (int)bbox.GetHeight();
      }
    }
    width = wxMax(width, w);
    height = wxMax(height, h);
  }
}

void MenuObject::UpdateSize()
{
  int width = GetWidth();
  int height = GetHeight();
  FixSize(width, height);
  SetWidth(width);
  SetHeight(height);
}

wxString MenuObject::GetParam(wxString name, wxString attrSuffix)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return wxT("");
  wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(param->element);
  if (!elem)
    return wxT("");
  if (param->attribute.length()) {
    // return attribute value
    if (param->attribute.Find(wxT(':')) > 0) {
       wxString value = elem->GetAttribute(param->attribute.BeforeFirst(wxT(':')));
       long n = -1;
       param->attribute.AfterFirst(wxT(':')).ToLong(&n);
       if (n > 0 && value.length() > 0 && value.Index(wxT('(')) > 0) {
         // return n-parameter of the function (e.g. if you want to get rotation angle of transform)
         value = value.AfterFirst(wxT('(')).BeforeFirst(wxT(')')).BeforeFirst(wxT(','));
         return value.Trim().Trim(false);
       }
    }
    return elem->GetAttribute(param->attribute + attrSuffix);
  } else if (elem->GetDtd() == wxSVG_TEXT_ELEMENT) {
    // return content of text element
    return elem->GetChildren() ? elem->GetChildren()->GetContent() : wxT("");
  }
  return wxT("");
}

void MenuObject::SetParam(wxString name, wxString value, wxString attrSuffix)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return;
  wxSVGElement* elem =(wxSVGElement*)  m_symbol->GetElementById(param->element);
  if (!elem)
    return;
  if (param->attribute.length()) {
    if (param->attribute.Find(wxT('#')) > 0) {
       wxString oldValue = elem->GetAttribute(param->attribute.BeforeFirst(wxT('#')));
       long n = -1;
       param->attribute.AfterFirst(wxT('#')).ToLong(&n);
       if (n > 0 && oldValue.length() > 0 && oldValue.Index(wxT('(')) > 0) {
         value = oldValue.BeforeFirst(wxT('(')) + wxT('(') + value;
         if (oldValue.Index(wxT(')')) < oldValue.Index(wxT(',')) && oldValue.Index(wxT(')')) > 0)
           value += wxT(')') + oldValue.AfterFirst(wxT(')'));
         else
           value += wxT(',') + oldValue.AfterFirst(wxT(','));
       }
       elem->SetAttribute(param->attribute.BeforeFirst(wxT('#')), value);
    } else
      elem->SetAttribute(param->attribute + attrSuffix, value);
  } else if (elem->GetDtd() == wxSVG_TEXT_ELEMENT) {
    if (!elem->GetChildren())
      return;
    elem->GetChildren()->SetContent(value);
    ((wxSVGTextElement*)elem)->SetCanvasItem(NULL);
  }
}

int MenuObject::GetParamInt(wxString name)
{
  long lval = 0;
  GetParam(name).ToLong(&lval);
  return lval;
}

void MenuObject::SetParamInt(wxString name, int value)
{
  SetParam(name, wxString::Format(wxT("%d"), value));
}

double MenuObject::GetParamDouble(wxString name) {
	double dval = 0;
	GetParam(name).ToDouble(&dval);
	return dval;
}

void MenuObject::SetParamDouble(wxString name, double value) {
	SetParam(name, wxString::Format(wxT("%g"), value));
}

wxFont MenuObject::GetParamFont(wxString name)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return wxFont(20, wxFONTFAMILY_DEFAULT, wxNORMAL, wxNORMAL, false);
  wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(param->element);
  if (!elem)
    return wxFont(20, wxFONTFAMILY_DEFAULT, wxNORMAL, wxNORMAL, false);
  
  int size = 20;
  double dval;
  if (elem->GetAttribute(wxT("font-size")).ToDouble(&dval))
    size = (int)dval;
  
  int style = wxFONTSTYLE_NORMAL;
  wxString styleStr = elem->GetAttribute(wxT("font-style"));
  if (styleStr == wxT("italic"))
    style = wxFONTSTYLE_ITALIC;
  else if (styleStr == wxT("oblique"))
    style = wxFONTSTYLE_SLANT;
  
  wxFontWeight weight = wxFONTWEIGHT_NORMAL;
  wxString weightStr = elem->GetAttribute(wxT("font-weight"));
  if (weightStr == wxT("bold"))
    weight = wxFONTWEIGHT_BOLD;
  if (weightStr == wxT("bolder"))
    weight = wxFONTWEIGHT_MAX;
  else if (weightStr == wxT("lighter"))
    weight = wxFONTWEIGHT_LIGHT;
  
  wxString faceName = elem->GetAttribute(wxT("font-family"));
  
  return wxFont(size, wxFONTFAMILY_DEFAULT, style, weight, false, faceName);
}

void MenuObject::SetParamFont(wxString name, wxFont value)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return;
  wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(param->element);
  if (!elem)
    return;
  
  elem->SetAttribute(wxT("font-size"),
    wxString::Format(wxT("%d"), value.GetPointSize()));
  
  wxString styleStr = wxT("normal");
  if (value.GetStyle() == wxFONTSTYLE_ITALIC)
    styleStr = wxT("italic");
  else if (value.GetStyle() == wxFONTSTYLE_SLANT)
    styleStr = wxT("oblique");
  elem->SetAttribute(wxT("font-style"), styleStr);
  
  wxString weightStr = wxT("normal");
  if (value.GetWeight() == wxFONTWEIGHT_BOLD)
    weightStr = wxT("bold");
  if (value.GetWeight() == wxFONTWEIGHT_MAX)
    weightStr = wxT("bolder");
  else if (value.GetWeight() == wxFONTWEIGHT_LIGHT)
    weightStr = wxT("lighter");
  elem->SetAttribute(wxT("font-weight"), weightStr);
  
  elem->SetAttribute(wxT("font-family"), value.GetFaceName());
}

wxColour MenuObject::GetParamColour(wxString name, MenuButtonState state)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return wxT("");
  if (param->changeable)
  {
    switch (state)
    {
      case mbsNORMAL: return param->normalColour;
      case mbsHIGHLIGHTED: return param->highlightedColour;
      case mbsSELECTED: return param->selectedColour;
    }
  }
  wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(param->element);
  if (!elem)
    return wxT("");
  if (param->attribute.length())
  {
    const wxCSSStyleDeclaration& style = wxSVGStylable::GetElementStyle(*elem);
    const wxCSSValue& value = style.GetPropertyCSSValue(param->attribute);
    switch (value.GetCSSValueType())
    {
      case wxCSS_PRIMITIVE_VALUE:
        return ((wxCSSPrimitiveValue&)value).GetRGBColorValue();
      case wxCSS_SVG_COLOR:
      case wxCSS_SVG_PAINT:
        return ((wxSVGColor&)value).GetRGBColor();
      default:
        break;
    }
  }
  return wxColour();
}

void MenuObject::SetParamColour(wxString name, wxColour value, MenuButtonState state)
{
  MenuObjectParam* param = GetObjectParam(name);
  if (!param)
    return;
  if (param->changeable)
  {
    switch (state)
    {
      case mbsNORMAL: param->normalColour = value; break;
      case mbsHIGHLIGHTED: param->highlightedColour = value; break;
      case mbsSELECTED: param->selectedColour = value; break;
    }
  }
  if (state != mbsNORMAL)
    return;
  wxSVGElement* elem = (wxSVGElement*) m_symbol->GetElementById(param->element);
  if (!elem)
    return;
  if (param->attribute.length())
  {
    wxSVGPaint paint(value);
    elem->SetAttribute(param->attribute, paint.GetCSSText());
  }
}

void MenuObject::ToFront() {
	wxSVGElement* parent = (wxSVGElement*) m_use->GetParent();
	parent->RemoveChild(m_use);
	parent->AppendChild(m_use);
}

void MenuObject::Forward() {
	wxSVGElement* parent = (wxSVGElement*) m_use->GetParent();
	wxSVGElement* next = (wxSVGElement*) m_use->GetNextSibling();
	if (next && next->GetType() == wxSVGXML_ELEMENT_NODE
			&& next->GetDtd() == wxSVG_USE_ELEMENT) {
    	parent->RemoveChild(m_use);
    	parent->InsertChild(m_use, next->GetNextSibling());
	}
}

void MenuObject::Backward() {
	wxSVGElement* parent = (wxSVGElement*) m_use->GetParent();
	wxSVGElement* prev = (wxSVGElement*) m_use->GetPreviousSibling();
	if (prev && prev->GetType() == wxSVGXML_ELEMENT_NODE
			&& prev->GetDtd() == wxSVG_USE_ELEMENT) {
		parent->RemoveChild(m_use);
		parent->InsertChild(m_use, prev);
	}
}

void MenuObject::ToBack() {
	wxSVGElement* parent = (wxSVGElement*) m_use->GetParent();
	wxSVGElement* first = (wxSVGElement*) parent->GetFirstChild();
	while (first && (first->GetType() != wxSVGXML_ELEMENT_NODE
				|| first->GetDtd() != wxSVG_USE_ELEMENT))
		first = (wxSVGElement*) first->GetNextSibling();
	if (first && first != m_use) {
		parent->RemoveChild(m_use);
		parent->InsertChild(m_use, first);
	}
}

bool MenuObject::IsFirst() {
	wxSVGElement* prev = (wxSVGElement*) m_use->GetPreviousSibling();
	return !prev || prev->GetType() != wxSVGXML_ELEMENT_NODE;
}

bool MenuObject::IsLast() {
	wxSVGElement* next = (wxSVGElement*) m_use->GetNextSibling();
	return !next || next->GetType() != wxSVGXML_ELEMENT_NODE;
}

wxImage MenuObject::GetImage(int maxWidth, int maxHeight)
{
  if (!m_use)
    return wxImage();
  m_svg->GetRootElement()->SetWidth(GetWidth());
  m_svg->GetRootElement()->SetHeight(GetHeight());
  return m_svg->Render(maxWidth, maxHeight);
}

wxSvgXmlNode* MenuObject::GetXML(DVDFileType type, bool withSVG, int playAllRegister)
{
  wxString rootName = wxT("object");
  if (IsButton())
    rootName = wxT("button");
  wxSvgXmlNode* rootNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, rootName);
  switch (type)
  {
	case DVDSTYLER_XML:
      if (IsButton())
      {
        wxSvgXmlNode* actionNode = MenuAction::GetXML(type, true);
        actionNode->AddProperty(_T("playAll"), IsPlayAll() ? wxT("true") : wxT("false"));
        actionNode->AddProperty(_T("audio"), wxString::Format(_T("%d"), GetAudio()));
        actionNode->AddProperty(_T("subtitle"), wxString::Format(_T("%d"), GetSubtitle()));
        rootNode->AddChild(actionNode);
      }
      if (GetDirection(0).length() || GetDirection(1).length() ||
          GetDirection(2).length() || GetDirection(3).length())
      {
        wxSvgXmlNode* directionNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, _T("direction"));
        if (GetDirection(0).length())
          directionNode->AddProperty(_T("left"), GetDirection(0));
        if (GetDirection(1).length())
          directionNode->AddProperty(_T("right"), GetDirection(1));
        if (GetDirection(2).length())
          directionNode->AddProperty(_T("up"), GetDirection(2));
        if (GetDirection(3).length())
          directionNode->AddProperty(_T("down"), GetDirection(3));
        rootNode->AddChild(directionNode);
      }
      XmlWriteValue(rootNode, _T("filename"), GetFileName().AfterLast(wxFILE_SEP_PATH));
      for (int i=0; i<GetObjectParamsCount(); i++)
      {
        MenuObjectParam* param = GetObjectParam(i);
        if (param->changeable)
        {
          wxSvgXmlNode* paramNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, _T("parameter"));
          paramNode->AddProperty(_T("name"), param->name);
          paramNode->AddProperty(_T("normal"),
            wxSVGPaint(param->normalColour).GetCSSText());
          paramNode->AddProperty(_T("highlighted"),
            wxSVGPaint(param->highlightedColour).GetCSSText());
          paramNode->AddProperty(_T("selected"),
            wxSVGPaint(param->selectedColour).GetCSSText());
          rootNode->AddChild(paramNode);
        }
      }
      rootNode->AddProperty(_T("id"), GetId());
      if (IsDefaultSize())
        rootNode->AddProperty(_T("defSize"), _T("true"));
      break;
	case SPUMUX_XML:
    {
      int mwidth = (int)m_svg->GetRootElement()->GetWidth().GetBaseVal()-1;
      int mheight = (int)m_svg->GetRootElement()->GetHeight().GetBaseVal()-1;
	  rootNode->AddProperty(_T("name"), GetId());
	  rootNode->AddProperty(_T("x0"), wxString::Format(_T("%d"),
        GetX() > 0 ? GetX() : 0));
	  rootNode->AddProperty(_T("y0"), wxString::Format(_T("%d"),
        GetY() > 0 ? GetY() : 0));
	  rootNode->AddProperty(_T("x1"), wxString::Format(_T("%d"),
        GetX() + GetWidth() < mwidth ? GetX() + GetWidth() : mwidth));
	  rootNode->AddProperty(_T("y1"), wxString::Format(_T("%d"),
        GetY() + GetHeight() < mheight ? GetY() + GetHeight() : mheight));
	  if (GetDirection(0).length())
		rootNode->AddProperty(_T("left"), GetDirection(0));
	  if (GetDirection(1).length())
		rootNode->AddProperty(_T("right"), GetDirection(1));
	  if (GetDirection(2).length())
		rootNode->AddProperty(_T("up"), GetDirection(2));
	  if (GetDirection(3).length())
		rootNode->AddProperty(_T("down"), GetDirection(3));
	  break;
    }
	case DVDAUTHOR_XML:
	{
	  rootNode->AddProperty(_T("name"), GetId());
	  wxString action = GetAction();
	  if (playAllRegister != -1)
	    action = wxString::Format(wxT("g%d=%d;"), playAllRegister, IsPlayAll()) + action;
	  rootNode->AddChild(new wxSvgXmlNode(wxSVGXML_TEXT_NODE, wxEmptyString, action));
	  break;
	}
	default:
	  break;
  }
  if (withSVG) // used by copy & paste
  {
    rootNode->DeleteProperty(wxT("id"));
    wxSvgXmlNode* svgNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, _T("svg"));
    wxSvgXmlNode* defsNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, _T("defs"));
    wxSVGElement* symbol = (wxSVGElement*) m_symbol->CloneNode();
    symbol->SetId(wxT(""));
    defsNode->AddChild(symbol);
    svgNode->AddChild(defsNode);
    svgNode->AddChild(m_use->CloneNode());
    rootNode->AddChild(svgNode);
  }
  return rootNode;
}

bool MenuObject::PutXML(wxSvgXmlNode* node)
{
  m_button = node->GetName() == wxT("button");
  
  long lval;
  wxString val;
    
  if (IsButton())
  {
    wxSvgXmlNode* actionNode = XmlFindNodeSimple(node, _T("action"));
    if (actionNode)
    {
      MenuAction::PutXML(actionNode, true);
      SetPlayAll(actionNode->GetPropVal(_T("playAll"), &val) && val == wxT("true"));
      if (actionNode->GetPropVal(_T("audio"), &val) && val.ToLong(&lval))
        SetAudio(lval);
      if (actionNode->GetPropVal(_T("subtitle"), &val) && val.ToLong(&lval))
        SetSubtitle(lval);
    }
    
    wxSvgXmlNode* dirNode = XmlFindNodeSimple(node, _T("direction"));
    if (dirNode)
    {
      if (dirNode->GetPropVal(_T("left"), &val))
        SetDirection(0, val);
      if (dirNode->GetPropVal(_T("right"), &val))
        SetDirection(1, val);
      if (dirNode->GetPropVal(_T("up"), &val))
        SetDirection(2, val);
      if (dirNode->GetPropVal(_T("down"), &val))
        SetDirection(3, val);
    }
  }
  
  // deprecated
  int x = 0, y = 0;
  if (node->GetPropVal(_T("x"), &val) && val.ToLong(&lval))
    x = (int)(lval/m_svg->GetScale());
  if (node->GetPropVal(_T("y"), &val) && val.ToLong(&lval))
    y = (int)(lval/m_svg->GetScale());
  wxString fileName = XmlReadValue(node, _T("type"));
  if (fileName.length())
  {
    wxString text = XmlReadValue(node, _T("text"));
    return Init(BUTTONS_DIR + wxT("text.xml"), x, y, text);
  }
  
  fileName = XmlReadValue(node, _T("filename"));
  if (!fileName.length())
    return false;
  
  wxFileName fn(fileName);
  if (fn.IsRelative()) {
    wxString dir = IsButton() ? BUTTONS_DIR : OBJECTS_DIR;
	if (wxFileName::FileExists(dir + fileName))
	  fileName = dir + fileName;
    else if (wxFileName::FileExists(dir + wxT("deprecated") + wxFILE_SEP_PATH + fileName))
	  fileName = dir + wxT("deprecated") + wxFILE_SEP_PATH + fileName;
    else
      return false;
  }
  
  node->GetPropVal(_T("id"), &m_id);
  if (!node->GetPropVal(_T("defSize"), &val) ||
      (val != wxT("true") && val != wxT("1")))
    SetDefaultSize(false);
  
  // paste button
  wxSvgXmlNode* svgNode = XmlFindNode(node, wxT("svg"));
  if (svgNode)
  {
    m_id = GenerateId(m_button ? wxT("button") : wxT("obj"));
    wxSVGDocument svg;
    LoadSVG(svg, svgNode);
    wxSVGSVGElement* root = svg.GetRootElement();
    if (root && XmlFindNode(root, wxT("defs")) && XmlFindNode(root, wxT("use")))
    {
      wxSVGElement* defsNode = (wxSVGElement*) XmlFindNode(root, wxT("defs"));
      wxSvgXmlElement* child = defsNode->GetChildren();
      while (child)
      {
        AddSymol(m_id, (wxSVGElement*) child);
        child = child->GetNext();
      }
      wxSVGUseElement* useElem = (wxSVGUseElement*) XmlFindNode(root, wxT("use"));
      AddUse(m_id, useElem->GetX().GetBaseVal(), useElem->GetY().GetBaseVal(),
        useElem->GetWidth().GetBaseVal(), useElem->GetHeight().GetBaseVal());
    }
  }
  
  if (!Init(fileName, x, y))
    return false;
  
  wxSvgXmlNode* child = node->GetChildren();
  while (child)
  {
    if (child->GetName() == wxT("parameter"))
    {
      wxString name;
      wxString normal;
      wxString selected;
      wxString highlighted;
      if (child->GetPropVal(wxT("name"), &name) &&
          child->GetPropVal(wxT("normal"), &normal) &&
          child->GetPropVal(wxT("highlighted"), &highlighted) &&
          child->GetPropVal(wxT("selected"), &selected))
      {
        wxCSSStyleDeclaration style;
        style.SetProperty(wxT("fill"), normal);
        SetParamColour(name, style.GetFill().GetRGBColor(), mbsNORMAL);
        style.SetProperty(wxT("fill"), highlighted);
        SetParamColour(name, style.GetFill().GetRGBColor(), mbsHIGHLIGHTED);
        style.SetProperty(wxT("fill"), selected);
        SetParamColour(name, style.GetFill().GetRGBColor(), mbsSELECTED);
      }
    }
    child = child->GetNext();
  }
  
  return true;
}
