#
#  Copyright (c) 1997-2003 The Protein Laboratory, University of Copenhagen
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#  SUCH DAMAGE.
#
#  $Id$
#

=pod 
=item NAME

A periodic table of elements

=item FEATURES

Demonstrates usage of grid widget

=cut

use strict;
use Prima qw(Application Grids);


my $w = Prima::MainWindow-> create(
	text => "Periodic table of elements",
	size => [ 50 * 14 + 25, 50 * 13 + 25],
);

my @layers = ( 2, 8, 8, 10, 9, 10, 9, 10, 9, 10, 0, 14, 14);
my %colors = (
	2 => {
		0 => cl::Blue,
		10 => cl::Red,
	},
	8 => {
		0 => cl::Blue,
		1 => cl::Blue,
		10 => cl::Red,
		default => 0x804000
	},
	9 => {
		0 => cl::Black,
		1 => cl::Black,
		10 => cl::Red,
		default => 0x804000
	},
	10 => {
		0 => cl::Blue,
		1 => cl::Blue,
		default => cl::Black,
	},
	14 => {
		default => cl::Green,
	}
);
my %sides = (
'9:0' => 4, '9:1' => 4, '9:2' => 4,
'7:4' => 8, '8:4' => 8, '9:4' => 12,
'7:6' => 8, '8:6' => 8, '9:6' => 12,
'7:8' => 8, '8:8' => 8, '9:8' => 12,
'10:3' => 8, '10:5' => 8, '10:7' => 8, '10:9' => 8,
'0:10' => 8, '1:10' => 8, '2:10' => 8, '3:10' => 8, '4:10' => 8,
'5:10' => 8, '6:10' => 8, '7:10' => 8, '8:10' => 8, '9:10' => 8,
#  '11:4' => 8, '12:4' => 8, '13:4' => 12,
#  '10:5' => 12, '13:5' => 4, '13:6' => 4,
#  '11:7' => 8, '12:7' => 8, '13:7' => 8,
);

package Periodic;
use vars qw(@ISA);
@ISA = qw(Prima::GridViewer);

my %elem_info = (
	H  => { atomic_number => 1, name => "Hydrogen", weight => "1.00794(7)", },
	He => { atomic_number => 2, name => "Helium", weight => "4.002602(2)", },

	Li => { atomic_number => 3, name => "Lithium", weight => "6.941(2)", },
	Be => { atomic_number => 4, name => "Beryllium", weight => "9.012182(3)", },
	B  => { atomic_number => 5, name => "Boron", weight => "10.881(7)", },
	C  => { atomic_number => 6, name => "Carbon", weight => "12.0107(8)", },
	N  => { atomic_number => 7, name => "Nitrogen", weight => "14.00674(7)", },
	O  => { atomic_number => 8, name => "Oxygen", weight => "15.9994(3)", },
	F  => { atomic_number => 9, name => "Fluorine", weight => "18.9984032(5)", },
	Ne => { atomic_number => 10, name => "Neon", weight => "20.1797(6)", },

	Na => { atomic_number => 11, name => "Sodium", weight => "22.989770(2)", },
	Mg => { atomic_number => 12, name => "Magnesium", weight => "24.3050(6)", },
	Al => { atomic_number => 13, name => "Aluminium", weight => "26.981538(2)", },
	Si => { atomic_number => 14, name => "Silicon", weight => "28.0855(3)", },
	P  => { atomic_number => 15, name => "Phosphorus", weight => "30.973761(2)", },
	S  => { atomic_number => 16, name => "Sulfur", weight => "32.066(6)", },
	Cl => { atomic_number => 17, name => "Chlorine", weight => "35.4527(9)", },
	Ar => { atomic_number => 18, name => "Argon", weight => "39.948(1)", },
	K  => { atomic_number => 19, name => "Potassium", weight => "39.0983(1)", },
	Ca => { atomic_number => 20, name => "Calcium", weight => "40.078(4)", },
	Sc => { atomic_number => 21, name => "Scandium", weight => "44.955910(8)", },
	Ti => { atomic_number => 22, name => "Titanium", weight => "47.867(1)", },
	V  => { atomic_number => 23, name => "Vanadium", weight => "50.9415(1)", },
	Cr => { atomic_number => 24, name => "Chromium", weight => "51.9961(6)", },
	Mn => { atomic_number => 25, name => "Manganese", weight => "54.938049(9)", },
	Fe => { atomic_number => 26, name => "Iron", weight => "55.845(2)", },
	Co => { atomic_number => 27, name => "Cobalt", weight => "58.933200(9)", },
	Ni => { atomic_number => 28, name => "Nickel", weight => "58.6934(2)", },
	Cu => { atomic_number => 29, name => "Copper", weight => "63.546(3)", },
	Zn => { atomic_number => 30, name => "Zinc", weight => "65.39(2)", },
	Ga => { atomic_number => 31, name => "Gallium", weight => "69.723(1)", },
	Ge => { atomic_number => 32, name => "Germanium", weight => "72.61(2)", },
	As => { atomic_number => 33, name => "Arsenic", weight => "74.92160(2)", },
	Se => { atomic_number => 34, name => "Selenium", weight => "78.96(3)", },
	Br => { atomic_number => 35, name => "Bromine", weight => "79.904(1)", },
	Kr => { atomic_number => 36, name => "Krypton", weight => "83.80(1)", },

	Rb => { atomic_number => 37, name => "Rubidium", weight => "85.4678(3)", },
	Sr => { atomic_number => 38, name => "Strontium", weight => "87.62(1)", },
	Y  => { atomic_number => 39, name => "Yttrium", weight => "88.90585(2)", },
	Zr => { atomic_number => 40, name => "Zirconium", weight => "91.224(2)", },
	Nb => { atomic_number => 41, name => "Niobium", weight => "92.90638(2)", },
	Mo => { atomic_number => 42, name => "Molybdenum", weight => "95.94(1)", },
	Tc => { atomic_number => 43, name => "Technetium", weight => "[97.9072]", },
	Ru => { atomic_number => 44, name => "Ruthenium", weight => "101.07(2)", },
	Rh => { atomic_number => 45, name => "Rhodium", weight => "102.90550(2)", },
	Pd => { atomic_number => 46, name => "Palladium", weight => "106.42(1)", },
	Ag => { atomic_number => 47, name => "Silver", weight => "107.8682(2)", },
	Cd => { atomic_number => 48, name => "Cadmium", weight => "112.411(8)", },
	In => { atomic_number => 49, name => "Indium", weight => "114.818(3)", },
	Sn => { atomic_number => 50, name => "Tin", weight => "118.710(7)", },
	Sb => { atomic_number => 51, name => "Antimony", weight => "121.760(1)", },
	Te => { atomic_number => 52, name => "Tellurium", weight => "127.60(3)", },
	I  => { atomic_number => 53, name => "Iodine", weight => "126.90447(3)", },
	Xe => { atomic_number => 54, name => "Xenon", weight => "131.29(2)", },

	Cs => { atomic_number => 55, name => "Caesium", weight => "132.90545(2)", },
	Ba => { atomic_number => 56, name => "Barium", weight => "137.327(7)", },
	La => { atomic_number => 57, name => "Lanthanum", weight => "138.9055(2)", },

	Hf => { atomic_number => 72, name => "Hafnium", weight => "178.49(2)", },
	Ta => { atomic_number => 73, name => "Tantalum", weight => "180.9479(1)", },
	W  => { atomic_number => 74, name => "Tungsten", weight => "183.84(1)", },
	Re => { atomic_number => 75, name => "Rhenium", weight => "186.207(1)", },
	Os => { atomic_number => 76, name => "Osmium", weight => "190.23(3)", },
	Ir => { atomic_number => 77, name => "Iridium", weight => "192.217(3)", },
	Pt => { atomic_number => 78, name => "Platinum", weight => "195.078(2)", },
	Au => { atomic_number => 79, name => "Gold", weight => "196.96655(2)", },
	Hg => { atomic_number => 80, name => "Mercury", weight => "200.59(2)", },
	Tl => { atomic_number => 81, name => "Thallium", weight => "204.3383(2)", },
	Pb => { atomic_number => 82, name => "Lead", weight => "207.2(1)", },
	Bi => { atomic_number => 83, name => "Bismuth", weight => "208.98038(2)", },
	Po => { atomic_number => 84, name => "Polonium", weight => "[208.9824]", },
	At => { atomic_number => 85, name => "Astatine", weight => "[209.9871]", },
	Rn => { atomic_number => 86, name => "Radon", weight => "[222.0176]", },

	Fr => { atomic_number => 87, name => "Francium", weight => "[223.0197]", },
	Ra => { atomic_number => 88, name => "Radium", weight => "[226.0254]", },
	Ac => { atomic_number => 89, name => "Actinium", weight => "[227.0277]", },

	Rf => { atomic_number => 104, name => "Rutherfordium", weight => "[263.1125]", },
	Db => { atomic_number => 105, name => "Dubnium", weight => "[262.1144]", },
	Sg => { atomic_number => 106, name => "Seaborgium", weight => "[266.1219]", },
	Bh => { atomic_number => 107, name => "Bohrium", weight => "[264.1247]", },
	Hs => { atomic_number => 108, name => "Hassium", weight => "[269.1341]", },
	Mt => { atomic_number => 109, name => "Meitnerium", weight => "[268.1388]", },
	Ds => { atomic_number => 110, name => "Darmstadtium", weight => "[272.1463]", },

	Ce => { atomic_number => 58, name => "Cerium", weight => "140.116(1)", },
	Pr => { atomic_number => 59, name => "Praseodymium", weight => "140.90765(2)", },
	Nd => { atomic_number => 60, name => "Neodymium", weight => "144.24(3)", },
	Pm => { atomic_number => 61, name => "Promethium", weight => "[144.9127]", },
	Sm => { atomic_number => 62, name => "Samarium", weight => "150.36(3)", },
	Eu => { atomic_number => 63, name => "Europium", weight => "151.964(1)", },
	Gd => { atomic_number => 64, name => "Gadolinium", weight => "157.25(3)", },
	Tb => { atomic_number => 65, name => "Terbium", weight => "158.92534(2)", },
	Dy => { atomic_number => 66, name => "Dysprosium", weight => "162.50(3)", },
	Ho => { atomic_number => 67, name => "Holmium", weight => "164.93032(2)", },
	Er => { atomic_number => 68, name => "Erbium", weight => "167.26(3)", },
	Tm => { atomic_number => 69, name => "Thulium", weight => "168.93421(2)", },
	Yb => { atomic_number => 70, name => "Ytterbium", weight => "173.04(3)", },
	Lu => { atomic_number => 71, name => "Lutetium", weight => "174.967(1)", },

	Th => { atomic_number => 90, name => "Thorium", weight => "232.0381(1)", },
	Pa => { atomic_number => 91, name => "Protactinium", weight => "231.03588(2)", },
	U  => { atomic_number => 92, name => "Uranium", weight => "238.0289(1)", },
	Np => { atomic_number => 93, name => "Neptunium", weight => "[237.0482]", },
	Pu => { atomic_number => 94, name => "Plutonium", weight => "[244.0642]", },
	Am => { atomic_number => 95, name => "Americium", weight => "[243.0614]", },
	Cm => { atomic_number => 96, name => "Curium", weight => "[247.0703]", },
	Bk => { atomic_number => 97, name => "Berkelium", weight => "[247.0703]", },
	Cf => { atomic_number => 98, name => "Californium", weight => "[251.0796]", },
	Es => { atomic_number => 99, name => "Einsteinium", weight => "[252.0830]", },
	Fm => { atomic_number => 100, name => "Fermium", weight => "[257.0951]", },
	Md => { atomic_number => 101, name => "Mendelevium", weight => "[258.0984]", },
	No => { atomic_number => 102, name => "Nobelium", weight => "[259.1011]", },
	Lr => { atomic_number => 103, name => "Lawrencium", weight => "[262.110]", },
);

sub focusedCell
{
	return $_[0]-> SUPER::focusedCell unless $#_;
	my ($self,$x,$y) = @_;
	($x, $y) = @$x if !defined $y && ref($x) eq 'ARRAY';
	return unless $y >= 0 && $x >= 0 && $self-> {cells}-> [$y] && 
		$self-> {cells}-> [$y]-> [$x] && length $self-> {cells}-> [$y]-> [$x];
	$self-> SUPER::focusedCell( $x, $y);
}

my @small_font_metrics;

my $g = $w-> insert( Periodic => 
	origin => [0,0],
	size   => [$w-> size],
	growMode => gm::Client,
	cells  => [
		['H',  ('')x9,                    'He',   ('')x3],
		[qw(Li Be B  C  N  O  F),  ('')x3,'Ne',   ('')x3],
		[qw(Na Mg Al Si P  S  Cl), ('')x3,'Ar',   ('')x3],
		[qw(K  Ca Sc Ti V  Cr Mn Fe Co Ni),       ('')x4],
		[qw(Cu Zn Ga Ge As Se Br), ('')x3,'Kr',   ('')x3],
		[qw(Rb Sr Y  Zr Nb Mo Tc Ru Rh Pd),       ('')x4],
		[qw(Ag Cd In Sn Sb Te I),  ('')x3,'Xe',   ('')x3],
		[qw(Cs Ba La Hf Ta W  Re Os Ir Pt),       ('')x4],
		[qw(Au Hg Tl Pb Bi Po At), ('')x3,'Rn',   ('')x3],
		[qw(Fr Ra Ac Rf Db Sg Bh Hs Mt Ds),       ('')x4],
		[('') x 14],
		[qw(Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu)],
		[qw(Th Pa U  Np Pu Am Cm Bk Cf Es Fm Md No Lr)],
	],
	drawHGrid => 0,
	drawVGrid => 0,
	constantCellWidth => 50,
	constantCellHeight => 50,
	multiSelect => 0,
	onDrawCell => sub {
		my ( $self, $canvas, 
			$column, $row, $indent, 
			$sx1, $sy1, $sx2, $sy2, 
			$cx1, $cy1, $cx2, $cy2, 
			$selected, $focused) = @_;
		$canvas-> clear($sx1, $sy1, $sx2, $sy2);
		my $item = $self-> {cells}-> [$row]-> [$column];
		my $color = $colors{$layers[$row]};
		if ( length $item) {
			return unless $color;
			return unless defined 
				($color = ( exists $color-> {$column}) ? 
					$color-> {$column} : 
					$color-> {default});
			$canvas-> color( cl::Black);
			$canvas-> rectangle( $cx1-1, $cy1-1, $cx2, $cy2);
			if ( $focused) {
				$canvas-> color( $self-> hiliteBackColor);
				$canvas-> bar( $cx1, $cy1, $cx2-1, $cy2-1);
				$canvas-> color( $self-> hiliteColor);
			} else {
				$canvas-> color( $color);
			}
			$canvas-> text_out( $item, $cx1 + 10, $cy1 + 10);
			my $f = $canvas-> font;
			$canvas-> font( height => 12);
				@small_font_metrics = ( $canvas-> get_text_width('3'), $f-> height)
					unless @small_font_metrics;
					
			my $text = $elem_info{$item}-> {atomic_number}||"";
			$canvas-> text_out( $text, 
				$cx2 - $small_font_metrics[0] * length($text) - 4, 
				$cy2 - $small_font_metrics[1] - 4);
			$canvas-> font($f);
			$canvas-> rect_focus( $sx1, $sy1, $sx2-1, $sy2-1) if $focused;
		} elsif ( exists $sides{"$column:$row"}) {
			my $side = $sides{"$column:$row"};
			$canvas-> color( cl::Black);
			$canvas-> line( $cx1-1,$cy1-1,$cx2,$cy1-1 ) if $side & 1;
			$canvas-> line( $cx1-1,$cy1-1,$cx1-1,$cy2 ) if $side & 2;
			$canvas-> line( $cx2,$cy1-1,$cx2,$cy2 ) if $side & 4;
			$canvas-> line( $cx1-1,$cy2,$cx2,$cy2 ) if $side & 8;
		}
	},
	onClick => sub {
			my ($self) = @_;
			my @foc = $self-> focusedCell;
			my $text = $self-> {cells}-> [$foc[1]]-> [$foc[0]];
			return unless $text;
			if ($text eq "La") {
				$self-> focusedCell(0, 11);
			} elsif ($text eq "Ac") {
				$self-> focusedCell(0, 12);
			}
	},
	onSelectCell => sub {
		my ( $self, $col, $row) = @_;
		my $item = $self-> {cells}-> [$row]-> [$col];
		return unless defined($item) and defined ($elem_info{$item}-> {name});
		$w-> text("Periodic table of elements - $elem_info{$item}->{name} $elem_info{$item}->{atomic_number} $elem_info{$item}->{weight}");
	},
);

run Prima;
