#!/usr/bin/perl -w

# menu_data.pl: generate the menu structure from menu_data.dat
# Copyright (c) 2004-2015 Philip Kendall, Stuart Brady, Marek Januszewski

# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# Author contact information:

# Philip Kendall <philip-fuse@shadowmagic.org.uk>

use strict;

use Fuse;

sub get_branch ($$);
sub cname ($);
sub dump_widget ($);
sub _dump_widget ($$);
sub dump_gtk ($$);
sub _dump_gtk ($$$$$);
sub dump_win32 ($$);
sub _dump_win32 ($$$$);

die "usage: $0 <ui>" unless @ARGV >= 1;

my $ui = shift;
my $filename;
my $mode = 'none';

# for Win32:
my $idmnum = 100;

if( $ui eq 'win32' ) {
  die "$0: dump_win32: which mode -- c, h, or rc?" unless @ARGV >= 1;

  $mode = shift;
  $filename = 'menu_data.' . $mode;
} elsif( $ui eq 'gtk' ) {
  die "$0: dump_gtk: which mode -- c, or ui?" unless @ARGV >= 1;

  $mode = shift;
  $filename = 'menu_data.' . $mode;
} else {
  $filename = 'menu_data.c';
}

print Fuse::GPL( $filename . ': menu structure for Fuse',
		 '2004-2007 Philip Kendall, Stuart Brady, Marek Januszewski' ) . << "CODE" if $mode ne 'ui';

/* This file is autogenerated from menu_data.dat by $0.
   Any changes made here will not be preserved. */

#include "config.h"

#include "menu.h"

CODE

my %menus = ( name => 'Main menu', submenu => [ ] );

while(<>) {

    s/#.*//;
    next if /^\s*$/;

    chomp;

    my( $path, $type, $hotkey, $function, $detail, $action ) = split /\s*,\s*/;

    my @segments = split '/', $path;

    my $entry = { };

    $entry->{name} = pop @segments;
    $entry->{type} = $type;
    $entry->{hotkey} = $hotkey if $hotkey;

    my $walk = $menus{submenu};
    $walk = get_branch( $walk, $_ ) foreach @segments;

    if( $type eq 'Branch' ) {

	$entry->{submenu} = [ ];

    } elsif( $type eq 'Item' ) {

	$entry->{function} = $function if $function;
	$entry->{action} = $action if $action;

    }

    $entry->{detail} = $detail if $detail;

    push @$walk, $entry;

}

if( $ui eq 'gtk' ) {
    dump_gtk( $mode, \%menus );
} elsif( $ui eq 'widget' ) {
    dump_widget( \%menus );
} elsif( $ui eq 'win32' ) {
    dump_win32( $mode, \%menus );
} else {
    die "$0: unknown ui: $ui";
}

sub get_branch ($$) {

    my( $menus, $branch ) = @_;

    foreach my $entry ( @$menus ) {

	next unless $entry->{submenu};

	my $name = $entry->{name};
	$name =~ s/_//;

	return $entry->{submenu} if $name eq $branch;
    }
}

sub cname ($) {

    my( $name ) = @_;

    my $cname = lc $name;
    $cname =~ s/\\01[12]//g if $ui eq 'widget';
    $cname =~ tr/a-z0-9_//cd;

    return $cname;
}

sub dump_widget ($) {

    my( $menu ) = @_;

    print << "HEADERS";
#include "input.h"
#include "ui/widget/options_internals.h"
#include "ui/widget/widget_internals.h"

HEADERS

    _dump_widget( $menu, 'menu' );

}

sub _dump_widget ($$) {

    my( $menu, $path ) = @_;

    my $menu_name = $menu->{name};
    $menu_name =~ s/_//;

    my $s;

    if( $path eq 'menu' ) {
	$s = 'widget_menu_entry widget_menu[]';
    } else {
    	# Slight ugliness here is because "${path}[]" doesn't work under
	# Perl 5.6
	$s = "static widget_menu_entry $path" . "[]";
    }

    $s .= " = {\n  { \"$menu_name\" },\n";

    foreach my $item ( @{ $menu->{submenu} } ) {

	next if $item->{type} eq 'Separator';

	my $name = $item->{name};
	my $key;
	if( $ui eq 'widget' ) {
	    $name =~ s/_(.)/\\012$1\\011/;
	    $key = lc $1 if $1;
	} else {
	    $name =~ s/_(.)/($1)/;
	    $key = lc $1 if $1;
	}
	
	$s .= "  { \"$name\", INPUT_KEY_" . ( $key || 'NONE' ) . ', ';

	my $cname = cname( $name );
	
        my $detail = $item->{detail} || "NULL";

	if( $item->{submenu} ) {
	    $s .= "${path}_$cname, NULL, $detail, 0";
	    _dump_widget( $item, "${path}_$cname" );
	} else {
	    my $function = $item->{function} || "${path}_$cname";
	    $s .= "NULL, $function, $detail, " . ( $item->{action} || 0 );
	}

	$s .= " },\n";
    }

    $s .= "  { NULL }\n};\n\n";

    print $s;
}

sub dump_gtk ($$) {

  my( $mode, $menu ) = @_;

  if( $mode eq 'c' ) {

    print << "HEADERS";
#include <gtk/gtk.h>

HEADERS

    print "/* Bindings to callbacks with action */\n";

    _dump_gtk( 'callbacks', $menu, '', 'menu', '  ' );

    print "GtkActionEntry gtkui_menu_data[] = {\n\n";

    _dump_gtk( 'actions', $menu, '', 'menu', '  ' );

    print << "CODE";

};

guint gtkui_menu_data_size = ARRAY_SIZE( gtkui_menu_data );
CODE

 } elsif( $mode eq 'ui' ) {

    print << "XML";
<?xml version="1.0" encoding="utf-8"?>
<ui>
  <menubar name='MainMenu'>
XML

    _dump_gtk( 'ui', $menu, '', 'menu', '    ' );

    print << "XML";
  </menubar>
</ui>
XML

  }
}

sub _dump_gtk ($$$$$) {

  my( $mode, $menu, $gtk_path, $cpath, $spaces ) = @_;

  foreach my $item ( @{ $menu->{submenu} } ) {

    next if $item->{type} eq 'Separator' && $mode ne 'ui';

    #Remove toplevel accelerators
    my $name = $item->{name};
    $name =~ s/_// if !$gtk_path;
    my $label = $name;

    my $action_name = $name;
    $action_name =~ s/_//;
    $action_name = $gtk_path . "/" . $action_name;
    $action_name =~ s|/|_|g;
    $action_name =~ s/_//;
    $action_name = uc( cname( $action_name ) );

    $name =~ s/_// if $gtk_path;
    my $new_cpath = "${cpath}_" . cname( $name );
    my $function;
    my $binded_function = $item->{function} || $new_cpath;

    if( $item->{type} eq 'Item' && $item->{action} ) {
      $function = $new_cpath;
    } else {
      $function = $binded_function;
    }

    if( $mode eq 'callbacks' ) {
      if( $item->{type} eq 'Item' && $item->{action} ) {
        print "static MENU_CALLBACK( ", $function, " )\n{\n";
        print "  $binded_function( gtk_action, $item->{action} );\n}\n\n";
      }
    } elsif( $mode eq 'ui' ) {
      if( $item->{type} eq 'Item' ) {
        print "$spaces<menuitem name='$name' action='$action_name'/>\n";
      } elsif( $item->{type} eq 'Separator' ) {
        print "$spaces<separator/>\n";
      } else {
        print "$spaces<menu name='", $name, "' action='", $action_name, "'>\n";
      }
    } else {
      #action_name, stock_id, label
      print "  { \"$action_name\", NULL, \"$label\", ";

      #hotkey
      if( $item->{hotkey} ) {
        print "\"$item->{hotkey}\"";
      } else {
        print 'NULL';
      }

      #tooltip
      print ", NULL, ";

      #callback
      if( $item->{type} eq 'Item' ) {
        print "G_CALLBACK( ", $function, " )";
      } else {
        print "NULL";
      }

      print " },\n";
    }

    _dump_gtk( $mode, $item, "$gtk_path/$name", $new_cpath, $spaces . "  " )
               if $item->{submenu};

    print "$spaces</menu>\n" if $mode eq 'ui' && $item->{type} eq 'Branch';
  }
}

sub dump_win32 ($$) {

    my( $mode, $menu ) = @_;

    if( $mode eq 'c' ) {
	print << "CODE";
#include "ui/win32/menu_data.h"
#include "ui/win32/win32internals.h"

int handle_menu( DWORD cmd, HWND window )
{
  switch( cmd )
  {
CODE
    } elsif( $mode eq 'h' ) {
	print << "CODE";
#include <windows.h>

CODE
    } elsif( $mode eq 'rc' ) {
	print << "CODE";
#include <windows.h>
#include "ui/win32/menu_data.h"

win32_menu MENU
{
CODE
    }

    _dump_win32( $mode, $menu, 'menu', '  ' );

    if( $mode eq 'c' ) {
	print << "CODE";
  }
  return 1;
}
CODE
    } elsif( $mode eq 'rc' ) {
	print << "CODE";
}

win32_accel ACCELERATORS
{
CODE
	_dump_win32( 'a', $menu, 'menu', '' );
	print << "CODE";
}
CODE
    }

}

sub _dump_win32 ($$$$) {

    my( $mode, $menu, $path, $spaces ) = @_;

    my $menu_name = $menu->{name};
    $menu_name =~ s/_//;

    foreach my $item ( @{ $menu->{submenu} } ) {

	if( $item->{type} eq 'Separator' ) {
	    if( $mode eq 'rc' ) {
		print "${spaces}MENUITEM SEPARATOR\n";
	    }
	    next;
	}

	my $name = $item->{name};

	$name =~ s/_/&/;

	my $cname = cname( $name );
	my $idmname = "IDM_" . uc( "${path}_$cname" );

	if( $mode eq 'h' ) {
	    print "#define $idmname $idmnum\n";
	    $idmnum++;
	} elsif( $mode eq 'a' ) {
	    if( $item->{hotkey} ) {
		print "  VK_" . uc( $item->{hotkey} ) . ", $idmname, VIRTKEY\n";
	    }
	}

	if( $item->{submenu} ) {
	    if( $mode eq 'rc' ) {
	        print "${spaces}POPUP \"$name\"\n";
		print "${spaces}\{\n";
	    }
	    _dump_win32( $mode, $item, "${path}_$cname", $spaces . "  " );
	    if( $mode eq 'rc' ) {
	        print "${spaces}}\n";
	    }
	} else {
	    if( $mode eq 'c' ) {
	        my $function = $item->{function} || "${path}_$cname";
		print "    case $idmname:\n";
		print "      $function( " . ( $item->{action} || 0 ) . " ); "
		      . "return 0;\n";
	    } elsif( $mode eq 'rc' ) {
		my $hotkey;
		if( $item->{hotkey} ) {
		    $hotkey = "\\t" . $item->{hotkey};
		} else {
		    $hotkey = "";
		}
		print "${spaces}MENUITEM \"$name$hotkey\", $idmname\n";
	    }
	}
    }
}
