#
# Copyright (C) 1997, 98, 99, 2000  Motoyuki Kasahara
#
# 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, 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.
#

# error message
set ndtp_error_message {}

# socket to a NDTP server
set ndtp_socket {}

# book ID list of the server
set ndtp_books {0}

# title list of the server
set ndtp_titles(0) [_ "(not selected)"]

# page information for each book
set ndtp_pages_menu(0) 0
set ndtp_pages_copyright(0) 0
set ndtp_pages_word(0) 0
set ndtp_pages_endword(0) 0
set ndtp_pages_keyword(0) 0

# bitmap size information for each book.
set ndtp_sizes_16(0) 0
set ndtp_sizes_24(0) 0
set ndtp_sizes_30(0) 0
set ndtp_sizes_48(0) 0

# Do `ndtp_pages_menu', `menu_copyright' and `ndtp_pages_word' in
# a book have valid values?
set ndtp_cache_indexes(0) 0

# Do `size_16' ... `size_48' for a book have valid values?
set ndtp_cache_sizes(0) 0

# Do bitmaps are created for a book?
set ndtp_cache_all_bitmaps(0) 0

# current book
set ndtp_current_book 0

# current bitmap size
set ndtp_current_size 0

# current text position
set ndtp_current_text_position ""

# created bitmap size
set ndtp_cache_size 0

# whether server accept bitmap extension command?
set ndtp_extension_ok {}

# default method
set ndtp_default_method "exactword"

#
# Examine whether a connection is active.
#
proc ndtp_is_active {} {
    global ndtp_socket

    if {$ndtp_socket != {}} {
	return 1
    } else {
	return 0
    }
}

#
# Initialize global variables.
#
proc ndtp_initialize {} {
    global ndtp_socket
    global ndtp_books
    global ndtp_current_book
    global ndtp_current_size
    global ndtp_current_text_position
    global ndtp_cache_size
    global ndtp_titles
    global ndtp_pages_menu
    global ndtp_pages_copyright
    global ndtp_pages_word
    global ndtp_pages_endword
    global ndtp_pages_keyword
    global ndtp_sizes_16
    global ndtp_sizes_24
    global ndtp_sizes_30
    global ndtp_sizes_48
    global ndtp_cache_indexes
    global ndtp_cache_sizes
    global ndtp_cache_all_bitmaps
    global ndtp_extension_ok

    #
    # Close the socket if active.
    #
    if {$ndtp_socket != {}} {
	catch {close $ndtp_socket}
    }

    #
    # Initialize global variables.
    #
    set ndtp_socket {}

    foreach i $ndtp_books {
	if {$i == 0} {
	    continue
	}
	catch {unset ndtp_titles($i)}
	catch {unset ndtp_pages_menu($i)}
	catch {unset ndtp_pages_copyright($i)}
	catch {unset ndtp_pages_word($i)}
	catch {unset ndtp_pages_endword($i)}
	catch {unset ndtp_pages_keyword($i)}
	catch {unset ndtp_sizes_16($i)}
	catch {unset ndtp_sizes_24($i)}
	catch {unset ndtp_sizes_30($i)}
	catch {unset ndtp_sizes_48($i)}
	catch {unset ndtp_cache_indexes($i)}
	catch {unset ndtp_cache_sizes($i)}
	catch {unset ndtp_cache_all_bitmaps($i)}
    }

    set ndtp_cache_size 0

    set ndtp_books {}
    set ndtp_extension_ok {}

    set ndtp_current_book 0
    set ndtp_current_size 0
    set ndtp_current_text_position ""
}

#
# Open a connection to a NDTP server.
#
proc ndtp_open {hostname port books} {
    global ndtp_error_message
    global ndtp_socket
    global ndtp_books
    global ndtp_titles
    global ndtp_cache_indexes
    global ndtp_cache_sizes
    global ndtp_cache_all_bitmaps

    upvar $books up_books
    set up_books {}

    #
    # Initialize global variables.
    #
    ndtp_initialize

    #
    # Delete bitmaps.
    #
    ndtp_delete_bitmaps

    #
    # Connect to the server.
    #
    if {[catch {set ndtp_socket [socket $hostname $port]} \
	    ndtp_error_message]} {
	ndtp_initialize
	return 0
    }
    fconfigure $ndtp_socket -translation {lf lf}
    
    #
    # Send the `A' command.
    # dserver-2.2betapl2 sends a debug message to a client (`dictnum=...').
    # BookView ignores it.
    #
    if {![ndtp_put_line "Abookview"]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    while {[string match {dictnum=*} $line]} {
	if {![ndtp_get_line line]} {
	    return 0
	}
    }
    if {$line != "\$A"} {
	if {$line == "\$N"} {
	    ndtp_close
	}
	ndtp_initialize
	set ndtp_error_message [_ "Authentication failed."]
	return 0
    }

    #
    # Send the `t' command.
    #
    if {![ndtp_put_line "t"]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    if {[regexp {^ *([0-9]+)	([^	]+)	([^	]+)	} \
	    $line dummy dummy title bookcode]} {
	lappend ndtp_books $bookcode
	set ndtp_titles($bookcode) $title
	set ndtp_cache_indexes($bookcode) 0
	set ndtp_cache_sizes($bookcode) 0
	set ndtp_cache_all_bitmaps($bookcode) 0
    } elseif {$line == "\$*"} {
        return 1
    } elseif {$line != "\$*"} {
	ndtp_initialize
	set ndtp_error_message [_ "Cannot get a book list."]
	return 0
    }
    while {1} {
	if {![ndtp_get_line line]} {
	    return 0
	}
	if {![regexp {^ *([0-9]+)	([^	]+)	([^	]+)	} \
		$line dummy dummy title bookcode]} {
	    break
	}
	lappend ndtp_books $bookcode
	set ndtp_titles($bookcode) $title
	set ndtp_cache_indexes($bookcode) 0
	set ndtp_cache_sizes($bookcode) 0
	set ndtp_cache_all_bitmaps($bookcode) 0
    }
    if {$line != "\$*"} {
	puts $line
	ndtp_initialize
	set ndtp_error_message [_ "Cannot get a book list."]
	return 0
    }

    set up_books $ndtp_books
    return 1
}

#
# Close a connection to the current server.
#
proc ndtp_close {} {
    global ndtp_socket
    global ndtp_error_message

    #
    # Send the `Q' command.
    #
    if {![ndtp_put_line "Q"]} {
	return 0
    }

    #
    # Close the connection.
    #
    if {[catch {close $ndtp_socket} ndtp_error_message]} {
	ndtp_initialize
	return 0
    }

    ndtp_initialize
    return 1
}

#
# Select a book on a server.
#
proc ndtp_select_book {book} {
    global ndtp_error_message
    global ndtp_books
    global ndtp_current_book
    global ndtp_current_size
    global ndtp_current_text_position
    global ndtp_pages_menu
    global ndtp_pages_copyright
    global ndtp_pages_word
    global ndtp_pages_endword
    global ndtp_pages_keyword
    global ndtp_sizes_16
    global ndtp_sizes_24
    global ndtp_sizes_30
    global ndtp_sizes_48
    global ndtp_cache_indexes
    global ndtp_cache_sizes
    global ndtp_extension_ok

    if {$book == "nodict" || $book == "0"} {
	set book 0
    } elseif {[lsearch -exact $ndtp_books $book] == -1} {
	set ndtp_error_message [format [_ "Unknown book %s."] $book]
	return 0
    }

    #
    # If $book has been selected, return immediately.
    #
    if {$book == $ndtp_current_book} {
	return 1
    }

    #
    # If this book has not selected yet, initialize page and
    # bitmap size tables.
    #
    if {!$ndtp_cache_indexes($book)} {
	set ndtp_pages_menu($book) 0
	set ndtp_pages_copyright($book) 0
	set ndtp_pages_word($book) 0
	set ndtp_pages_endword($book) 0
	set ndtp_pages_keyword($book) 0
    }
    if {!$ndtp_cache_sizes($book)} {
	set ndtp_sizes_16($book) 0
	set ndtp_sizes_24($book) 0
	set ndtp_sizes_30($book) 0
	set ndtp_sizes_48($book) 0
    }

    #
    # Send the `L' command.
    #
    set ndtp_current_book 0
    set ndtp_current_size 0
    set ndtp_current_text_position ""
    if {![ndtp_put_line "L$book"]} {
	set ndtp_cache_indexes($book) 0
	set ndtp_cache_sizes($book) 0
	return 0
    }
    if {![ndtp_get_line line]} {
	set ndtp_cache_indexes($book) 0
	set ndtp_cache_sizes($book) 0
	return 0
    }
    if {$line != "\$*"} {
	if {![string match {$?} $line]} {
	    ndtp_initialize
	}
	set ndtp_error_message [format [_ "Cannot select the book %s."] $book]
	set ndtp_cache_indexes($book) 0
	set ndtp_cache_sizes($book) 0
	return 0
    }
    set ndtp_current_book $book

    #
    # If $book is 0, skip the following procedures.
    #
    if {$book == 0} {
	return 1
    }

    #
    # Send the `I' command.
    # (If fails, give up talking to the server.)
    #
    if {!$ndtp_cache_indexes($ndtp_current_book)} {
	if {![ndtp_put_line "I"]} {
	    return 0
	}
	if {![ndtp_get_line line]} {
	    return 0
	}
	if {$line != "\$I"} {
	    ndtp_initialize
	    set ndtp_error_message [format \
		    [_ "Cannot get a method list of %s."] $book]
	    return 0
	}

	while {1} {
	    if {![ndtp_get_line line]} {
		return 0
	    }
	    if {![regexp {^([A-Z]+) ([0-9A-Fa-f]+)} $line dummy method page]} {
		break
	    }
	    switch -exact $method {
		"IA" {set ndtp_pages_word($ndtp_current_book) $page}
		"BA" {set ndtp_pages_endword($ndtp_current_book) $page}
		"JO" {set ndtp_pages_keyword($ndtp_current_book) $page}
		"HA" {set ndtp_pages_menu($ndtp_current_book) $page}
		"OK" {set ndtp_pages_copyright($ndtp_current_book) $page}
	    }
	}
	if {$line != "\$\$"} {
	    ndtp_initialize
	    set ndtp_error_message [format \
		    [_ "Cannot get a method list of %s."] $book]
	    return 0
	}
	set ndtp_cache_indexes($ndtp_current_book) 1
    }

    #
    # Send the `XI' command.
    # Return with a success code even when the server responds with
    # the error code `unknown command'.
    #
    if {!$ndtp_cache_sizes($ndtp_current_book) && $ndtp_extension_ok != 0} {
	if {![ndtp_put_line "XI"]} {
	    return 0
	}
	if {![ndtp_get_line line]} {
	    return 0
	}
	if {$line == "\$?"} {
	    set ndtp_extension_ok 0
	} else {
	    if {$line != "\$I"} {
		if {[string match {$?} $line]} {
		    ndtp_select_book 0
		} else {
		    ndtp_initialize
		}
		set ndtp_error_message [format \
			[_ "Cannot get a bitmap size list of %s."] $book]
		return 0
	    }
	    set ndtp_extension_ok 1
	    while {1} {
		if {![ndtp_get_line line]} {
		    return 0
		}
		if {![regexp {^([0-9]+)} $line dummy size]} {
		    break
		}
		switch -exact $size {
		    "16" {set ndtp_sizes_16($ndtp_current_book) 1}
		    "24" {set ndtp_sizes_24($ndtp_current_book) 1}
		    "30" {set ndtp_sizes_30($ndtp_current_book) 1}
		    "48" {set ndtp_sizes_48($ndtp_current_book) 1}
		}
	    }
	    if {$line != "\$\$"} {
		ndtp_initialize
		set ndtp_error_message [format \
			[_ "Cannot get a bitmap size list of %s."] $book]
		return 0
	    }
	}
	set ndtp_cache_sizes($ndtp_current_book) 1
    }

    return 1
}

#
# Search a word.
#
proc ndtp_query_word {word positions headings} {
    global ndtp_error_message
    global ndtp_default_method
    upvar $positions up_positions
    upvar $headings up_headings

    set up_positions {}
    set up_headings {}

    #
    # Delete newline character and its follows in the word.
    # Delete whitespaces in the beginning and tail of the word.
    #
    regsub -all "\n" $word "" word
    regsub -all "\t" $word " " word
    regsub {^ *(.*) *$} $word {\1} word

    #
    # Adjust $word according with $ndtp_default_method.
    #
    if {$ndtp_default_method == "keyword" \
	    && ![kanji string match {*;*} $word]} {
	set word "$word;"
    } elseif {$ndtp_default_method == "endword" \
	    && ![kanji string match {\**} $word]} {
	set word "*$word"
    } elseif {$ndtp_default_method == "word" \
	    && ![kanji string match {*\*} $word]} {
	set word "$word*"
    }

    #
    # Set a request line.
    #
    if {[kanji string match {*;*} $word]} {
	regsub -all {[ 	]*;[ 	]*} $word {	} word
	set line "Pj$word"
    } elseif {[kanji string match {\**} $word]} {
	set length [kstring length $word]
	set reversed_word ""
	for {set i 0} {$i < $length} {incr i} {
	    set c [kstring index $word $i]
	    set reversed_word "$c$reversed_word"
	}
	if {[kanji code $word] == {ANY}} {
	    set line "PA$reversed_word"
	} else {
	    set line "PK$reversed_word"
	}
    } else {
	if {[kanji code $word] == {ANY}} {
	    set line "Pa$word"
	} else {
	    set line "Pk$word"
	}
    }

    #
    # Send the "P" command.
    #
    if {![ndtp_put_line $line]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    if {$line != "\$0"} {
	if {![string match {$?} $line]} {
	    ndtp_initialize
	}
	set ndtp_error_message [_ "Cannot search the word."]
	return 0
    }
    while {1} {
	if {![ndtp_get_line heading]} {
	    return 0
	}
	if {$heading == "\$\$"} {
	    break
	}
	if {![ndtp_get_line position]} {
	    return 0
	}
	lappend up_positions $position
	lappend up_headings $heading
    }

    return 1
}    

#
# Get text at `position' in the current selected book.
#
proc ndtp_get_text {position text} {
    global ndtp_error_message
    global ndtp_current_book
    global ndtp_pages_menu
    global ndtp_pages_copyright
    global ndtp_current_text_position
    upvar $text up_text

    set up_text {}

    #
    # Get a real position for menu and copyright request.
    #
    switch -exact $position {
	"menu" {
	    if {$ndtp_pages_menu($ndtp_current_book) == 0} {
		set ndtp_error_message [_ "This book doesn't have a menu"]
		return 0
	    }
	    set position "$ndtp_pages_menu($ndtp_current_book):0"
	}
	"copyright" {
	    if {$ndtp_pages_copyright($ndtp_current_book) == 0} {
		set ndtp_error_message [_ "This book doesn't have a menu"]
		return 0
	    }
	    set position "$ndtp_pages_copyright($ndtp_current_book):0"
	}
	"forward" {
	    set position [ndtp_get_forward_position]
	}
	"backward" {
	    set position [ndtp_get_backward_position]
	}
    }

    if {$position == ""} {
	return 0
    }

    #
    # Send the `S' command.
    #
    if {![ndtp_put_line "S$position"]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    if {$line != "\$1"} {
	if {![string match {$?} $line]} {
	    ndtp_initialize
	}
	set ndtp_error_message [_ "Cannot get text."]
	return 0
    }

    while {1} {
	if {![ndtp_get_line line]} {
	    return 0
	}
	regsub -all "\015\005" $line "\n" line
	if {$line == "\$\$"} {
	    break
	}
	append up_text $line "\n"
    }

    set ndtp_current_text_position $position

    return 1
}

#
# Get text position of next paragraph.
#
proc ndtp_get_forward_position {} {
    global ndtp_error_message
    global ndtp_current_book
    global ndtp_pages_menu
    global ndtp_pages_copyright
    global ndtp_current_text_position

    #
    # Send the `XS' command.
    #
    if {$ndtp_current_text_position == ""} {
	return ""
    }
    if {![ndtp_put_line "XS+$ndtp_current_text_position"]} {
	return $ndtp_current_text_position
    }
    if {![ndtp_get_line line]} {
	return $ndtp_current_text_position
    }
    if {![string match {\$XS*} $line]} {
	return $ndtp_current_text_position
    }
    regsub {^\$XS} $line "" line

    return $line
}

#
# Get text position of previous paragraph.
#
proc ndtp_get_backward_position {} {
    global ndtp_error_message
    global ndtp_current_book
    global ndtp_pages_menu
    global ndtp_pages_copyright
    global ndtp_current_text_position

    #
    # Send the `XS' command.
    #
    if {$ndtp_current_text_position == ""} {
	return ""
    }
    if {![ndtp_put_line "XS-$ndtp_current_text_position"]} {
	return $ndtp_current_text_position
    }
    if {![ndtp_get_line line]} {
	return $ndtp_current_text_position
    }
    if {![string match {\$XS*} $line]} {
	return $ndtp_current_text_position
    }
    regsub {^\$XS} $line "" line
    return $line
}

#
# Select a bitmap size, and get all bitmap data.
#
proc ndtp_select_bitmap_size {size {load_mode "pre-load"}} {
    global ndtp_error_message
    global ndtp_sizes_16
    global ndtp_sizes_24
    global ndtp_sizes_30
    global ndtp_sizes_48
    global ndtp_current_book
    global ndtp_current_size
    global ndtp_cache_size
    global ndtp_cache_all_bitmaps

    #
    # If $size has been selected, return immediately.
    #
    if {$size == $ndtp_current_size} {
	return 1
    }

    #
    # Send the `XL' command.
    #
    set ndtp_current_size 0
    if {![ndtp_put_line "XL$size"]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    if {$line != "\$*"} {
	if {$line != "\$<" && $line != "\$&"} {
	    ndtp_initialize
	}
	set ndtp_error_message [_ "Cannot select the font."]
	return 0
    }
    set ndtp_current_size $size

    #
    # If $size is 0, skip the following procedures.
    #
    if {$size == 0} {
	return 1
    }

    #
    # Delete bitmaps if bitmas of different size have been created.
    #
    if {$size != $ndtp_cache_size} {
	ndtp_delete_bitmaps
    }

    #
    # Send the `XB' command.
    # Return with a success code even when the server responds with
    # the error code `unknown command'.
    #
    if {$load_mode != "pre-load"} {
	set ndtp_cache_size $size
    } elseif {!$ndtp_cache_all_bitmaps($ndtp_current_book)} {
	if {![ndtp_put_line "XB"]} {
	    return 0
	}
	if {![ndtp_get_line line]} {
	    return 0
	}
	if {$line != "\$I"} {
	    if {$line != "\$<" && $line != "\$&" && $line != "\$?"} {
		ndtp_initialize
	    } else {
		ndtp_select_bitmap_size {}
	    }
	    set ndtp_error_message [_ "Cannot get font images."]
	    return 0
	}

	set xbmdata {}
	set imageid {}
	while {1} {
	    if {![ndtp_get_line line]} {
		return 0
	    }
	    if {$line == "\$\$"} {
		break
	    } elseif {[regexp {^\$=([A-Za-z0-9]+)$} $line dummy newid]} {
		if {$imageid != {}} {
		    image create bitmap "$ndtp_current_book/$imageid" \
			    -data $xbmdata
		}
		set imageid $newid
		set xbmdata {}
	    } else {
		append xbmdata $line "\n"
	    }
	}
	if {$imageid != {}} {
	    image create bitmap "$ndtp_current_book/$imageid" -data $xbmdata
	}
	set ndtp_cache_size $size
	set ndtp_cache_all_bitmaps($ndtp_current_book) 1
    }

    return 1
}

#
# Get bitmap data of character `imageid', if there is no cache data.
#
proc ndtp_get_bitmap {imageid} {
    global ndtp_error_message
    global ndtp_current_book
    global ndtp_current_size

    #
    # Return immediately if bitmap size is not selected.
    #
    if {$ndtp_current_size == 0} {
	return 0
    }

    #
    # Return immediately if we have already got the cache data.
    #
    if {![catch {image type "$ndtp_current_book/$imageid"}]} {
	return 1
    }

    #
    # Send the `Xb' command.
    #
    if {![ndtp_put_line "Xb$imageid"]} {
	return 0
    }
    if {![ndtp_get_line line]} {
	return 0
    }
    if {$line != "\$I"} {
	if {$line != "\$<" && $line != "\$&" && $line != "\$?"} {
	    ndtp_initialize
	} else {
	    ndtp_select_bitmap_size {}
	}
	set ndtp_error_message [_ "Cannot get a font image."]
	return 0
    }

    set xbmdata {}
    while {1} {
	if {![ndtp_get_line line]} {
	    return 0
	}
	if {$line == "\$\$"} {
	    break
	} else {
	    append xbmdata $line "\n"
	}
    }

    if {$xbmdata != {}} {
	image create bitmap "$ndtp_current_book/$imageid" -data $xbmdata
	return 1
    } else {
	return 0
    }

    # not reached
}

#
# Delete images.
#
proc ndtp_delete_bitmaps {} {
    global ndtp_cache_size

    foreach i [image names] {
	if {[string match "*/*" $i]} {
	    image delete $i
	}
    }

    set ndtp_cache_size 0
}

#
# The title of $book.
#
proc ndtp_title {book} {
    global ndtp_books
    global ndtp_titles

    if {$book == 0 || $book == "nodict"} {
	return [_ "(not selected)"]
    } elseif {[lsearch -exact $ndtp_books $book] == -1} {
	return ""
    } else {
	return $ndtp_titles($book)
    }

    # not reached
}

#
# A list of books.
#
proc ndtp_books {} {
    global ndtp_books

    return $ndtp_books
}

#
# Does the current book have word search?
#
proc ndtp_have_word_search {book} {
    global ndtp_pages_word

    if {$ndtp_pages_word($book) != 0} {
	return 1
    } else {
	return 0
    }
}

#
# Does the current book have endword search?
#
proc ndtp_have_endword_search {book} {
    global ndtp_pages_endword

    if {$ndtp_pages_endword($book) != 0} {
	return 1
    } else {
	return 0
    }
}

#
# Does the current book have endword search?
#
proc ndtp_have_keyword_search {book} {
    global ndtp_pages_keyword

    if {$ndtp_pages_keyword($book) != 0} {
	return 1
    } else {
	return 0
    }
}

#
# Does the current book have menu?
#
proc ndtp_have_menu {book} {
    global ndtp_pages_menu

    if {$ndtp_pages_menu($book) != 0} {
	return 1
    } else {
	return 0
    }
}

#
# Does the current book have copyright notice?
#
proc ndtp_have_copyright {book} {
    global ndtp_pages_copyright

    if {$ndtp_pages_copyright($book) != 0} {
	return 1
    } else {
	return 0
    }
}

#
# Receive a line from a NDTP server.
#
proc ndtp_get_line {line} {
    global tcl_platform
    global ndtp_error_message
    global ndtp_socket
    upvar $line up_line

    if {[catch {gets $ndtp_socket up_line} ndtp_error_message]} {
	ndtp_initialize
	return 0
    } elseif {[eof $ndtp_socket]} {
	ndtp_initialize
	set ndtp_error_message [_ "The server closes the connection."]
	return 0
    }

    if {$tcl_platform(platform) != "unix"} {
	set up_line [kanji conversion EUC SJIS $up_line]
    }

    return 1
}

#
# Send a line to a NDTP server.
#
proc ndtp_put_line {line} {
    global tcl_platform
    global ndtp_error_message
    global ndtp_socket

    if {$tcl_platform(platform) != "unix"} {
	set line [kanji conversion SJIS EUC $line]
    }

    if {[catch {puts $ndtp_socket $line} ndtp_error_message]} {
	ndtp_initialize
	return 0
    }
    if {[catch {flush $ndtp_socket} ndtp_error_message]} {
	ndtp_initialize
	return 0
    }

    return 1
}

#
# Set default method.
#
proc ndtp_set_default_method {method} {
    global ndtp_default_method

    if {$method == "exactword" \
	    || $method == "word" \
	    || $method == "endword" \
	    || $method == "keyword"} {
	set ndtp_default_method $method
    }
}

