#!/usr/sbin/perl
###
### 季節イベントのスケジュールをつくる
###
###
#
$dbfile = "schedule_event.lst";
$deffile = "m_ev_sche_def.h";
$datfile = "m_ev_sche_dat.h";
$evfile = "m_event.h";

#
## セーブレジスタ
#
%savereg = ( TD,   0,
             LPD,  1,
             BTD,  2,
             SP0,  3,
             SP1,  4,
             SP2,  5,
             WKY,  6,
             SP3,  7 );
$savedef = <<___ENDODSAVEDEF;
#define EV_SAVE_TODAY        ( 0)
#define EV_SAVE_LASTPLAYDAY  ( 1)
#define EV_SAVE_BIRTHDAY     ( 2)
#define EV_SAVE_SPECIAL0     ( 3)
#define EV_SAVE_SPECIAL1     ( 4)
#define EV_SAVE_SPECIAL2     ( 5)
#define EV_SAVE_WEEKLY0      ( 6)
#define EV_SAVE_SPECIAL3     ( 7)
___ENDODSAVEDEF
#
## 曜日
#
%weekday = (
        "日", 0,        "月", 1,        "火", 2,
        "水", 3,        "木", 4,        "金", 5,
        "土", 6,        "全", 7
    );
$weekdef = <<___ENDOFWEEKDEF;
#define EV_WEEK_SUNDAY    (0)
#define EV_WEEK_MONDAY    (1)
#define EV_WEEK_TUESDAY   (2)
#define EV_WEEK_WEDNESDAY (3)
#define EV_WEEK_THURSDAY  (4)
#define EV_WEEK_FRIDAY    (5)
#define EV_WEEK_SATURDAY  (6)
#define EV_WEEK_ANYDAY    (7)  /* 何曜日でも */
___ENDOFWEEKDEF

$everymonth = 50;
$lastday = 99;
$specialdef = <<___ENDOFSPECIALDEF;
#define EV_SPDATE_EVERYMONTH   (50)
#define EV_SPDATE_LASTDAY      (99)
___ENDOFSPECIALDEF

$defmax = 32;
$commax = 26;

open(DBF, $dbfile)
     || die "データベースをオープンできません。 '$dbfile'\n";
$i = 0;
while(<DBF>){
    next if /^!/;
    ($comment, $label, $date, $time) = split;
    if($label ne ""){
        if($COMMENT{$label} eq ""){
            $COMMENT{$label} = $comment;
        }elsif(!($COMMENT{$label} =~  /etc$/)){
            $COMMENT{$label} = $COMMENT{$label}." etc";
        }
        $COMM[$i] = $comment;
        $LABEL[$i] = $label;
        $DATE[$i] = $date;
        $TIME[$i] = $time;
        $i++;
    }
}
close(DBF);
$max = $i;

#
##
##  定義ヘッダーファイル書き出し
##
#
open(DEF, ">".$deffile) 
    || die "ヘッダーファイルをオープンできません。 '$deffile'\n";
print DEF <<____DEF_HEAD;
/*
 ******************************************************************************
 *
 *	スケジュールイベント管理（定義）
 *
 ******************************************************************************
 */

#ifndef	__M_EV_SCHE_DEF_H__
#define	__M_EV_SCHE_DEF_H__

/*
** スケジュールイベント定義 ** */
____DEF_HEAD
$i = 0;
foreach $key (sort keys(%COMMENT)){
    $enum = "EV_".$key;
    printf(DEF "#define %-${defmax}s (%3d) /* %-${commax}s */\n",  $enum, $i, $COMMENT{$key});
    $i++;
}
printf(DEF "#define %-${defmax}s (%3d) /* %-${commax}s */\n\n", "EV_SCHEDULE_NO_END", $i, "以上");
printf(DEF "#define EV_SCHEDULE_TYPE_MAX   EV_SCHEDULE_NO_END\n\n\n");

printf(DEF "#if defined(USE_EVENT_COMMENT_4_DEBUG) && defined(__M_EVENT_C__)\n");
printf(DEF "/*\n** デバッグ用文字列 ** */\nconst char *event_comment[EV_SCHEDULE_TYPE_MAX] = {\n");
foreach $key (sort keys(%COMMENT)){
    printf(DEF "   \"%s\",\n",  $COMMENT{$key});
    $i++;
}
printf(DEF "};\n#endif\n\n");

print DEF <<____DEF_TAIL;

/*
** --- スケジュール管理オブジェクト --- 
*/
typedef struct _event_date_data_ {
    char  month, day, flag, hour;
} evdate_t;

typedef union _event_schedule_date_ {
    evdate_t d;               /* 日付時間 */
    unsigned short v, f;      /* インテル互換なし */
    unsigned long lv;
} evdate_u;

typedef struct _event_schedule_ {
    evdate_u  day[2];
    short  f, type;
} EvSchedule;


#if defined(__M_EVENT_C__)
#if 0
#define is_normalday_data(flag)   ((flag) == 0)      /* 月日型データ */
#define is_kyureki_data(flag)     ((flag) & 0x08)    /* 旧暦か？ */
#define is_week_data(flag)        ((flag) & 0x01)    /* 週曜日指定か？ */
#define is_every_data(flag)       ((flag) & 0x06)    /* 毎？データ */
#define is_everyweek_data(flag)   ((flag) & 0x02)    /* 毎週データ */
#define is_everymonth_data(flag)  ((flag) & 0x04)    /* 毎月データ */
#endif
#define day_of_week(day)          ((day) & 0x07)     /* 曜日ゲット */
#define cycle_of_week(day)        (((day) & 0x78) >> 3) /* 何週目か？ */
#endif
/* flag */
#define EV_FLG_WDAY               (0x01)  /* 曜日 */
#define EV_FLG_WEEKLY             (0x02)  /* 毎週 */
#define EV_FLG_MONTHLY            (0x04)  /* 毎月 */
#define EV_FLG_JPOLD              (0x08)  /* 旧暦 */
/* alias */
#define EV_DATE_MONTH_DAY         (0x00)  /* ？月？日 */
#define EV_DATE_MONTH_N_WDAY      (0x01)  /* ？月第ｎ？曜日 */
#define EV_DATE_MONTH_WEEKLY      (0x03)  /* ？月毎週？曜日 */
#define EV_DATE_DAY               (0x04)  /* 毎月？日 */
#define EV_DATE_N_WDAY            (0x05)  /* 毎月第ｎ？曜日 */
#define EV_DATE_WEEKLY            (0x07)  /* 毎月毎週？曜日 */
#define EV_DATE_JP_MONTH_DAY      (0x08)  /* 旧暦？月？日 */

$specialdef

/*
** 曜日 ** */
$weekdef

/*
** 第？週 ** */
#define EV_WEEK_FIRST     (0x01)
#define EV_WEEK_SECOND    (0x02)
#define EV_WEEK_THIRD     (0x03)
#define EV_WEEK_FOURTH    (0x04)
#define EV_WEEK_FIFTH     (0x05)
#define EV_WEEK_LAST      (0x0f)  /* 最終？曜日 */
#define EV_WEEK_EVELY     (0x09)  /* 毎週 */

/*
** セーブレジスタ ** */
$savedef

#endif /* __M_EV_SCHE_DEF_H__ */

____DEF_TAIL
close(DEF);

#
##
##  データヘッダーファイル書き出し
##
#

open(DAT, ">".$datfile) 
    || die "ヘッダーファイルをオープンできません。 '$datfile'\n";
print DAT <<____DAT_HEAD;
/*
 ******************************************************************************
 *
 *	スケジュールイベント管理（データ）
 *
 ******************************************************************************
 */

#ifndef	__M_EV_SCHE_DAT_H__
#define	__M_EV_SCHE_DAT_H__
#ifdef  __M_EVENT_C__ 
#include "$deffile"

____DAT_HEAD
printf(DAT "static EvSchedule event_schedule_data[] =  {\n");
for($i = 0; $i < $max; $i++){
    $enum = "EV_".$LABEL[$i];
    $flg = "";
    ($n, $fdd, $fdf, $tdd, $tdf) = &make_date_data($DATE[$i], $flg);
    ($ftd, $ftf, $ttd, $ttf) = &make_time_data($TIME[$i], $flg);
    printf(DAT "  /* *** %s *** %s %s *** */\n",
	   $COMM[$i], $DATE[$i], $TIME[$i]);
    printf(DAT " {{{%s, 0x%02x, %s},{%s, 0x%02x, %s}}, %d, %s},\n",
	   $fdd, $fdf | $ftf, $ftd, $tdd, $tdf | $ttf, $ttd, $n, $enum);
}

printf(DAT "};\n#define EV_SCHEDULE_DATA_MAX  (%d)\n\n", $max);
printf(DAT "/*\n** 噂のメッセージテーブルを旧システムにあわす ** */\n");
printf(DAT "static int  event_rumor_table[] = {\n");
open(EVH, $evfile)
    || die "ヘッダーファイルをオープンできません。 '$evfile'\n";
while(<EVH>){
    if(/mEv_(RMR_\w+)_e/){
	if($COMMENT{$1}){
	    printf(DAT "    %-${defmax}s /* %s */\n",
			 "EV_".$1.",", "mEv_".$1."_e");
	}else{
	    printf(STDERR "噂のメッセージテーブルが一致しません。(%s)\n",
			 "mEv_".$1."_e");
	    printf(DAT "    %-${defmax}s /* %s */\n",
			 "EV_SCHEDULE_NO_END,", "× mEv_".$1."_e ×");
	}
    }
}
close(EVH);
printf(DAT "};\nstatic  int n_event_rumors = sizeof(event_rumor_table) / sizeof(int);\n\n");
print DAT <<____DAT_TAIL;
#endif /* __M_EVENT_C__ */
#endif /* __M_EV_SCHE_DAT_H__ */

____DAT_TAIL
close(DAT);

exit(0);

#
##
##  サブルーチン
##
#
sub make_date_data {
    local($date) = @_;
    local($ss) = "{ ";
    local($s, $f);
    local($c) = 0;
    local($n) = 0;
    local(@sa) = ( "", "" );
    local(@fl) = ( "", "" );
    while(($pos = index($date, "-")) >= $[){
	$d = substr($date, 0, $pos);
	if($d ne '$TD'){
	    ( $s, $f ) = &check_date($d, $c);
	    if($s ne ""){
		$sa[$c] = $s;  $fl[$c] = $f;  $c++;
	    }
	}else{
	    $n = $c + 1;
	}
	$date = substr($date, $pos + 1);
    }
    # 最後の
    if($date ne '$TD'){
	( $s, $f ) = &check_date($date, $c);
	if($s ne ""){
	    $sa[$c] = $s;  $fl[$c] = $f;  $c++;
	}
    }else{
	$n = $c + 1;
    }
    # 一つしかなかったら、もう一度同じデータで
    if($c <= 1){
	( $s, $f ) = &check_date($date, $c);
	if($s ne ""){
	    $sa[$c] = $s;  $fl[$c] = $f;  $c++;
	}	
    }
    return  ( $n, $sa[0], $fl[0], $sa[1], $fl[1] );
}

sub make_time_data {
    local($time) = @_;
    local($ss) = "{ ";
    local($s, $f);
    local($c) = 0;
    local(@sa) = ( "", "" );
    local(@fl) = ( "", "" );
    while(($pos = index($time, "-")) >= $[){
	$d = substr($time, 0, $pos);
	( $s, $f ) = &check_time($d, $c);
	if($s ne ""){
	    $sa[$c] = $s;  $fl[$c] = $f;  $c++;
	}
	$time = substr($time, $pos + 1);
    }
    # 最後の
    ( $s, $f ) = &check_time($time, $c);
    if($s ne ""){
	$sa[$c] = $s;  $fl[$c] = $f;  $c++;
    }
    # 一つしかなかったら、もう一度同じデータで
    if($c <= 1){
	( $s, $f ) = &check_time($time, $c);
	if($s ne ""){
	    $sa[$c] = $s;  $fl[$c] = $f;  $c++;
	}	
    }
    return  ( $sa[0], $fl[0], $sa[1], $fl[1] );
}

sub check_time {
    local($time, $dir) = @_;
    local($ss) = "";
    local($s) = "";
    local($f) = 0;
    if($time =~ /^終日$/){
	$s = sprintf("%3d", ($dir ? 23 : 0));
    }elsif($time =~ /^午前$/){
	$s = sprintf("%3d", ($dir ? 11 : 0));
    }elsif($time =~ /^午後$/){
	$s = sprintf("%3d", ($dir ? 23 : 12));
    }elsif($time =~ /^\$([A-Z0-9]+)$/){
	$s = sprintf("%3d", $savereg{$1}|0x20);
    }elsif($time =~ /^=([0-9][0-9])(:00|時)$/){
        $s = sprintf("%3d", $1|0x80);
    }elsif($time =~ /^@([0-9][0-9])(:00|時)$/){
        $s = sprintf("%3d", $1|0x40);
    }elsif($time =~ /^([0-9][0-9])(:00|時)$/){
        $s = sprintf("%3d", $1);
    }elsif($time =~ /^([0-9][0-9])(:|時)([0-9][0-9])(分)?$/){
        print STDERR "分はサポートしてません。時間単位にしてください。\n";
        $s = sprintf("%3d", $1);
    }else{
        print STDERR "時間データがおかしいです。($time)\n";
        $s = "99";
    }
    return  ( $s, $f );
}

sub check_date {
    local($date, $dir) = @_;
    local (%weekn) = (
        "毎週", 0x09, "最終", 0x0f,
        "第１", 0x01, "第２", 0x02, "第３", 0x03, "第４", 0x04, "第５", 0x05
    );
    local (%weekf) = (
        "毎週", 3, "最終", 1,
        "第１", 1, "第２", 1, "第３", 1, "第４", 1, "第５", 1
    );
    local($s) = "";
    local($f) = 0;

    # 前処理
    $date =~ s|^毎週|$everymonth月毎週|;
    $date =~ s|毎月|$everymonth/|;
    $date =~ s|末日|$lastday|;

    # 変換
    if($date =~ /^年中$/){
	$s = sprintf("%3d,%3d", ($dir ? 12 : 1), ($dir ? 31 : 1));
    }elsif($date =~ /^\$([A-Z0-9]+)$/){
	$s = sprintf("%3d,  0", 100 + $savereg{$1});
    }elsif($date =~ /^([0-9][0-9])(\/|月)([0-9][0-9])(日)?$/){
        $s = sprintf("%3d,%3d", $1, $3);
    }elsif($date =~ /^旧(暦)?([0-9][0-9])(\/|月)([0-9][0-9])(日)?$/){
        $s = sprintf("%3d,%3d", $2 + 80, $4);
#	$f = 8;
   }elsif($date =~ /^([0-9][0-9])(月)?(最終|第１|第２|第３|第４|第５)週$/){
        $s = sprintf("%3d,%3d", $1,
                0x80 | ($weekn{$3} << 3) | ($dir ? $weekday{'土'} : $weekday{'日'}));
#	$f = $weekf{$3};
   }elsif($date =~ /^([0-9][0-9])(月)?(毎週|最終|第１|第２|第３|第４|第５)(日|月|火|水|木|金|土)(曜日)?$/){
        $s = sprintf("%3d,%3d", $1, 0x80 | ($weekn{$3} << 3) | $weekday{$4});
#	$f = $weekf{$3};
    }else{
        print STDERR "日付データがおかしいです。($date)\n";
        $s = " 255, 255";
	$f = 0;
    }
    return  ( $s, $f );
}
