/* -*- mode: c++; tab-width: 4; c-basic-offset: 4 -*- */
/*
 * Copyright (C) 2005 Atmark <atmarkat _AT_ msn _DOT_ com>
 *                    AGAWA Koji <i _AT_ atty _DOT_ jp>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <qstringlist.h>
#include <qpixmap.h>
#include <qdir.h>
#ifdef QTOPIA
#include <qpe/config.h>
#else
#include <config.h>
#endif
#include "skin.h"
#include "debug.h"

/*!
  \page skin スキン

  メインウィンドウ下部に位置する操作パネルはカスタマイズを行うことができます。
  背景画像やボタン画像などをパーツを用意し、skin.iniファイルでパーツを配置する
  形になっています。

  <h1>skin.ini</h1>

  QtopiaのConfigクラスを使って読み込んでます。不正なフォーマットであった場合な
  ど、空データでファイルが上書きされる可能性があるので、スキン作成中は常にバッ
  クアップを取ってください。

  標準の設定は以下のとおりです。

  \include skin/skin.ini

  <h2>[global]セクション</h2>

  スキン全体の設定です。

  <dl>
   <dt>resolution</dt>
   <dd>値は \a VGA または \a QVGA です。そのスキンがVGA用なのかQVGA用なのかを定義します。
       VGAスキンをQVGAデバイスで使うときは縮小表示され、その逆は拡大表示されま
	   す。ただし、まじめに拡大縮小処理はしないので、自分のデバイスに応じたスキ
	   ンを使うのが好ましいです。</dd>
  </dl>

  <h2>[landscape], [portrait]セクション</h2>

  実際のスキンを定義するセクションです。

  SL-Cシリーズの2Wayスタイルに対応するために同じ機能を持ったセクションが二つあ
  ります。インプットスタイルでは [landscape]セクション、ビュースタイルでは
  [portrait]セクションの定義が使われます。もし片方しか定義されなければ、どちら
  のスタイルでもそのセクションの定義が使われ、その結果デザインが崩れます。

  基本的に、

    <パーツ名> = <パーツの配置X座標>, <パーツの配置Y座標>, <参照画像ファイル名>, <参照像内でのX座標>, <参照像内でのY座標>, <幅>, <高さ>

  という形を取っています。

  参照画像には bmp,gif,png,jpg などひととおり使えます。透明情報(またはアルファ値)がある画像フォーマットでは、それもちゃんと利用されます。

  <table> <tr>
   <td>パーツ名</td>
   <td>名称</td>
   <td>詳細</td>
  </tr> <tr>
   <td>background</td>
   <td>背景画像</td>
   <td>操作パネルのサイズはパーツのサイズと等しくなります。</td>
  </tr> <tr>
   <td>stop</td>
   <td>停止ボタン</td>
   <td>参照画像に押していない状態/押し込んだ状態の順で2つの画像を横に並べておく必要があります。 ボタン1個分の幅を指定します。</td>
  </tr> <tr>
   <td>previous</td>
   <td>前ボタン</td>
   <td>上に同じ</td>
  </tr> <tr>
   <td>next</td>
   <td>次ボタン</td>
   <td>上に同じ</td>
  </tr> <tr>
   <td>play</td>
   <td>再生ボタン</td>
   <td>上に同じ / 一時停止中は表示されません。</td>
  </tr> <tr>
   <td>pause</td>
   <td>一時停止ボタン</td>
   <td>上に同じ / 再生中は表示されません。</td>
  </tr> <tr>
   <td>current_playtime</td>
   <td>現在の再生位置</td>
   <td>参照画像に"<em>0123456789-:</em>"の順でフォントを横に並べておく必要があります。1文字分の幅を指定します。停止中は表示されません。</td>
  </tr> <tr>
   <td>total_playtime</td>
   <td>メディアのlength</td>
   <td>上に同じ</td>
  </tr> </table>
*/



/*!
  スキンパーツを作成します。
*/
SkinParts::SkinParts(Skin::PartsType type, const QRect& geometry, const QPixmap &image, int imageCount, int length, int section, void *data)
	: type_(type),
	  data_(data),
	  geometry_(geometry),
	  image_(image),
	  imageCount_(imageCount),
	  section_(section),	  
	  length_(length)
{
}

/*!

*/
SkinParts::~SkinParts()
{
}

/*!
  スキンパーツの種類を返します。

  \sa Skin::PartsType
*/
Skin::PartsType SkinParts::type() const
{
	return type_;
}

/*!
  スキンパーツの配置位置とサイズを返します。
*/
const QRect& SkinParts::geometry() const
{
	return geometry_;
}

/*!
  スキンパーツのPixmapを返します。

  Pixmapの内容はパーツによって違いますので、受け取った側で適切に処理する必要が
  あります。

  例) Skin::Play -> ボタンのPixmapが横に2つ並んでいる
      Skin::CurrentPlayTime -> 文字のPixmapが横に複数並んでいる
*/
const QPixmap& SkinParts::image() const
{
	return image_;
}

void * SkinParts::data() const
{ 
	return data_;
}

int SkinParts::imageCount() const 
{
	return imageCount_;
}

int SkinParts::length() const 
{
	return length_;
}

int SkinParts::section() const 
{
	return section_;
}

void SkinParts::setLength(int length)
{
	length_ = length;
}

// ================================================================

const Skin::PartsSetupInfo Skin::partsSetupInfo_[] = {
	{ Background, FlexibleParts, 1, "background" },
	{ Play, FixedParts, 2, "play" },
	{ Pause, FixedParts, 2, "pause" },
	{ Stop, FixedParts, 2, "stop" },
	{ Next, FixedParts, 2, "next" },
	{ Previous, FixedParts, 2, "previous" },
	{ SeekKnob, FixedPartsWithSpecificLength, 1, "knob" },
	{ VolumeKnob, FixedPartsWithSpecificLength, 1, "volknob" },
	{ CurrentPlayTime, FixedParts, 12, "current_playtime" },
	{ TotalPlayTime, FixedParts, 12, "total_playtime" },
	{ ToolBackground, FlexibleParts, 1, "tool_background" },
	{ ToolInfo, FixedRect, 1, "tool_info" },
	{ ToolRandomOff, FixedParts, 2, "tool_randomoff" },
	{ ToolRandomOn, FixedParts, 2, "tool_randomon" },
	{ ToolRepeatOff, FixedParts, 2, "tool_repeatoff" },
	{ ToolRepeatOne, FixedParts, 2, "tool_repeatone" },
	{ ToolRepeatAll, FixedParts, 2, "tool_repeatall" },
	{ ToolOverviewOff, FixedParts, 2, "tool_overviewoff" },
	{ ToolOverviewOn, FixedParts, 2, "tool_overviewon" },
	{ ToolViewPlayList, FixedParts, 2, "tool_viewplaylist" },
	{ ToolViewPlayInfo, FixedParts, 2, "tool_viewplayinfo" },
	{ PlaylistBackground, FlexibleParts, 1, "playlist_background" },
	{ PlaylistPLOn, FixedParts, 2, "playlist_viewplayliston" },
	{ PlaylistPLOff, FixedParts, 2, "playlist_viewplaylistoff" },
	{ PlaylistDynPlaylistEditorOn, FixedParts, 2, "playlist_viewdynplaylisteditoron" },
	{ PlaylistDynPlaylistEditorOff, FixedParts, 2, "playlist_viewdynplaylisteditoroff" },
	{ PlaylistOnTheGoPLOn, FixedParts, 2, "playlist_viewonthegoplayliston" },
	{ PlaylistOnTheGoPLOff, FixedParts, 2, "playlist_viewonthegoplaylistoff" },
	{ PlaylistCopy, FixedParts, 3, "playlist_copy" },
	{ PlaylistCut, FixedParts, 3, "playlist_cut" },
	{ PlaylistPaste, FixedParts, 3, "playlist_paste" },
	{ PlaylistDelete, FixedParts, 3, "playlist_delete" },
	{ PlaylistCopyToOnTheGoPL, FixedParts, 3, "playlist_copytoonthegoplaylist" },
	{ PlaylistResetFilterAndSorting, FixedParts, 3, "playlist_resetfilterandsorting" },
	{ PlaylistOpen, FixedParts, 2, "playlist_open" },
	{ PlaylistSave, FixedParts, 2, "playlist_save" },
	{ PlaylistModeSingle, FixedParts, 2, "playlist_modesingle" },
	{ PlaylistModeMultiSelect, FixedParts, 2, "playlist_modemultiselect" },
	{ PlaylistModeMoveItem, FixedParts, 3, "playlist_modemoveitem" },
	{ MAX, MAXCLASS, 0, 0 }
};

/*!
  \a imageDir はスキン用の画像があるディレクトリを指定します。
*/
Skin::Skin(Config &c, QString imageDir)
	: isValid_(false)
{
	parts_.resize(MAX);

	// imageDirの末尾にディレクトリセパレータを追加する
	if (imageDir[imageDir.length() - 1] != QDir::separator())
		imageDir += QDir::separator();

	// スキンのロード中のみ使われるQPixmapのキャッシュ
	QDict<QImage> images;
	images.setAutoDelete(true);

	const PartsSetupInfo *info = partsSetupInfo_;
	while (info->count != 0) {
		if (!setupParts(info, c, images, imageDir))
			return;
		++info;
	}

	images.clear();
	
	isValid_ = true;
}

Skin::~Skin()
{
}

bool Skin::setupParts(const PartsSetupInfo *info, Config& c, QDict<QImage> &images, const QString &imageDir)
{
	if (!c.hasKey(info->key)) {
		qFatal("Invalid skin: key <%s> not contains", info->key);
		return false;
	}

	QStringList value = c.readListEntry(info->key, ',');
	
	if (info->partsclass == FixedRect)
	{
		// TODO: Implement section handling for fixed rect parts...
		int x = value[0].toInt();
		int y = value[1].toInt();
		int width = value[2].toInt();
		int height = value[3].toInt();
		parts_.insert(info->type, new SkinParts(info->type, QRect(x, y, width, height), NULL, 0, 0, 0, NULL));
	}
	else
	{
		if (value.count() < 7)
		{
			qFatal("Invalid format: <%s=%s>", info->key, value.join(",").latin1());
			return false;
		}
	
		QImage *image = images.find(value[2]);
		if (!image)
		{
			image = new QImage();
			image->setAlphaBuffer(true);
			image->load(imageDir + value[2]);
			
			DPRINTF("%d x %d", image->width(), image->height());
			
			if (image->isNull()) {
				qFatal("Reference image <%s> not found", (imageDir + value[2]).latin1());
				return false;
			}
			images.insert(value[2], image);
		}
	
		int x = value[0].toInt();
		int y = value[1].toInt();
		int refX = value[3].toInt();
		int refY = value[4].toInt();
		int refWidth = value[5].toInt();
		int refHeight = value[6].toInt();
		int refLength = 0;
		int refSection = 0;
		void *refData = NULL;

		// Is there a length defined ?
		if (info->partsclass == FixedPartsWithSpecificLength && value.count() >= 8)
		{
			refLength = value[7].toInt();
			
			if (value.count() == 9 && value[8].startsWith("#"))
				refSection = value[8].mid(1).toInt();
		}
		else if (info->partsclass == FixedParts && value.count() == 8 && value[7].startsWith("#"))
		{
			refSection = value[7].mid(1).toInt();
		}
		else if (info->partsclass == FlexibleParts && value.count() == 8)
		{
			SkinSections *sections = new SkinSections();
			sections->setAutoDelete(true);
			
			QStringList sectionList = QStringList::split(':', value[7]);
			sections->resize(sectionList.count());
			uint xPos = 0;
			
	        for (uint i = 0; i < sectionList.count(); ++i)
	        {
	        	SkinSection *section = new SkinSection();
	        	QString sectionEntry = sectionList[i];
	        		        	
	        	if (sectionEntry.startsWith("t"))
	        	{
	        		section->setType(SkinSection::FlexibleTiled);
	        		section->setWidth(sectionEntry.mid(1).toInt());
	        		DPRINTF("flex  section->width() : %d", section->width());
	        	}
	        	else
	        	{
	        		section->setType(SkinSection::Fixed);
	        		section->setWidth(sectionEntry.toInt());
	        		DPRINTF("fixed section->width() : %d", section->width());
	        	}

	        	DPRINTF("xPos : %d", xPos);
	        	section->setX(xPos);
	        	
        		xPos += section->width();        		
        		        		
	        	sections->insert(i, section);
	        }			
			
			refData = static_cast<void *>(sections);
		}
	
		QPixmap partsImage;
		partsImage.convertFromImage(image->copy(refX, refY, refWidth * info->count, refHeight));
	
		DPRINTF("%s", (imageDir + value[2]).latin1());
	
		parts_.insert(info->type, new SkinParts(info->type, QRect(x, y, refWidth, refHeight), partsImage, info->count, refLength, refSection, refData));
	}
	
	return true;
}

/*!
  スキンの読み込みに成功していれば \a true を返します。
*/
bool Skin::isValid() const
{
	return isValid_;
}

/*!
  \a type で指定された種類のスキンパーツを返します。
*/
const SkinParts *Skin::lookup(PartsType type) const
{
	return parts_[type];
}