#                                                         -*- Perl -*-
# Copyright (c) 1999, 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.
#

#
# 単語一覧を収めたファイルを整列するためのクラス。
#
package FreePWING::Sort;

require 5.005;
require Exporter;
use FileHandle;
use English;
use strict;
use integer;

use vars qw(@ISA
	    @EXPORT
	    @EXPORT_OK
	    $sort);

@ISA = qw(Exporter);

# sort コマンド
$sort = '/usr/bin/sort';

#
# 書式:
#	new()
# メソッドの区分:
# 	public クラスメソッド。
# 説明:
# 	新しいオブジェクトを作る。
# 戻り値:
# 	作成したオブジェクトへのリファレンスを返す。
#
sub new {
    my $type = shift;
    my $new = {
	# 単語整列ファイルのハンドラ
	'handle' => FileHandle->new(),

	# 単語整列ファイル名
	'file_name' => '',

	# これまでに書き込んだエントリ数
	'entry_count' => 0,

	# エラーメッセージ
	'error_message' => '',
    };
    return bless($new, $type);
}

#
# 書式:
#	open(file_name)
#           file_name
#		開く単語整列ファイルの名前。
# メソッドの区分:
# 	public インスタンスメソッド
# 説明:
# 	書き込み用に単語整列ファイルを開く。
# 戻り値:
#	成功すれば 1 を返す。失敗すれば 0 を返す。
#
sub open {
    my $self = shift;
    my ($file_name) = @ARG;
    my ($lc_all, $lc_ctype, $lc_collate, $lc_time, $lc_numeric, $lc_monetary,
	$lc_message);

    #
    # 現在のロケール環境変数を保存する。
    #
    $lc_all      = $ENV{'LC_ALL'};
    $lc_collate  = $ENV{'LC_COLLATE'};
    $lc_ctype    = $ENV{'LC_CTYPE'};
    $lc_time     = $ENV{'LC_TIME'};
    $lc_numeric  = $ENV{'LC_NUMERIC'};
    $lc_monetary = $ENV{'LC_MONETARY'};
    $lc_message  = $ENV{'LC_MESSAGE'};

    #
    # ロケール環境変数を修正する。
    #
    if (defined($lc_all)) {
	$ENV{'LC_TIME'} = $lc_all;
	$ENV{'LC_NUMERIC'} = $lc_all;
	$ENV{'LC_MONETARY'} = $lc_all;
	$ENV{'LC_MESSAGE'} = $lc_all;
	delete($ENV{'LC_ALL'});
    }
    $ENV{'LC_CTYPE'} = 'C';
    $ENV{'LC_COLLATE'} = 'C';

    #
    # 単語整列ファイルを開く。
    #
    $self->{'file_name'} = $file_name;
    if (!$self->{'handle'}->open("| $sort > $file_name")) {
	$self->{'error_message'} = 
	    "failed to open the file, $ERRNO: " . $self->{'file_name'};
	$self->close_internal();
	return 0;
    }

    #
    # ロケール環境変数を元に戻す。
    #
    defined($lc_all)
	? ($ENV{'LC_ALL'}      = $lc_all)      : delete($ENV{'LC_ALL'});
    defined($lc_collate)
	? ($ENV{'LC_COLLATE'}  = $lc_collate)  : delete($ENV{'LC_COLLATE'});
    defined($lc_ctype)
	? ($ENV{'LC_CTYPE'}    = $lc_ctype)    : delete($ENV{'LC_CTYPE'});
    defined($lc_time)
	? ($ENV{'LC_TIME'}     = $lc_time)     : delete($ENV{'LC_TIME'});
    defined($lc_numeric)
	? ($ENV{'LC_NUMERIC'}  = $lc_numeric)  : delete($ENV{'LC_NUMERIC'});
    defined($lc_monetary)
	? ($ENV{'LC_MONETARY'} = $lc_monetary) : delete($ENV{'LC_MONETARY'});
    defined($lc_message)
	? ($ENV{'LC_MESSAGE'}  = $lc_message)  : delete($ENV{'LC_MESSAGE'});

    return 1;
}

#
# 書式:
#	close()
# メソッドの区分:
# 	public インスタンスメソッド。
# 説明:
# 	単語整列ファイルを閉じる。開いていなければ、何もしない。
# 戻り値:
#	常に 1 を返す。
#
sub close {
    my $self = shift;

    return $self->close_internal();
}

#
# 書式:
#	close_internal()
# メソッドの区分:
# 	private インスタンスメソッド。
# 説明:
#	close() の内部処理用メソッド。
#
sub close_internal {
    my $self = shift;

    if ($self->{'handle'}->fileno()) {
	$self->{'handle'}->close();
	if (($CHILD_ERROR & 255) != 0) {
	    if ($self->{'error_message'} eq '') {
		$self->{'error_message'} =
		    "failed to close the file: " . $self->{'file_name'};
	    }
	    return 0;
	}
    }

    return 1;
}

#
# 書式:
#	add_entries_in_file(file_name)
#           file_name
#		インデックスのエントリが記されたファイル。
# メソッドの区分:
# 	public インスタンスメソッド。
# 説明:
# 	file_name の中に記されている全エントリを、インデックスファイル
#	群に対して追加する。
# 戻り値:
#	成功すれば 1 を返す。失敗すれば 0 を返す。
#
sub add_entries_in_file {
    my $self = shift;
    my ($file_name) = @ARG;

    #
    # $file_name を開く。
    #
    my $handle = FileHandle->new();
    if (!$handle->open($file_name, 'r')) {
	$self->{'error_message'} =
	    "failed to open the file, $ERRNO: $file_name";
	$self->close_internal();
	return 0;
    }

    #
    # $file_name を読み、各行に記されているエントリを $self に追加
    # する。
    #
    my $line;
    for (;;) {
	$line = $handle->getline();
	if (!defined($line)) {
	    last;
	}
	if (!$self->{'handle'}->print($line)) {
	    $self->{'error_message'} = "failed to write the file, $ERRNO: "
		. $self->{'file_name'};
	    $self->close_internal();
	    return 0;
	}

	$self->{'entry_count'}++;
    }

    #
    # $file_name のハンドルを閉じる。
    #
    $handle->close();

    return 1;
}

#
# 書式:
#	add_entry(word, heading_position, heading_file_name,
#		  text_position, text_file_name)
#           word
#		単語 (正規化されたもの)
#           heading_position
# 		見出しの位置
#           heading_file_name
# 		見出しのファイル名
#           text_position
# 		本文の位置
#           text_file_name
# 		本文のファイル名
# メソッドの区分:
# 	public インスタンスメソッド。
# 説明:
# 	単語ファイルに単語を一つ追加する。
# 戻り値:
#	成功すれば 1 を返す。失敗すれば 0 を返す。
#
sub add_entry {
    my $self = shift;
    my ($word, $heading_position, $heading_file_name, $text_position,
	$text_file_name) = @ARG;

    if (!$self->{'handle'}
	->printf("%s\t%08x\t%s\t%08x\t%s\n", $word, $heading_position,
		 $heading_file_name, $text_position, $text_file_name)) {
	$self->{'error_message'} =
	    "failed to write the file, $ERRNO: " . $self->{'file_name'};
	$self->close_internal();
	return 0;
    }

    $self->{'entry_count'}++;
    return 1;
}

######################################################################
# <インスタンス変数の値を返すメソッド群>
#
# 書式:
#	インスタンス変数名()
# メソッドの区分:
# 	public インスタンスメソッド。
# 戻り値:
#	インスタンス変数の値を返す。
#
sub file_name {
    my $self = shift;
    return $self->{'file_name'};
}

sub entry_count {
    my $self = shift;
    return $self->{'entry_count'};
}

sub error_message {
    my $self = shift;
    return $self->{'error_message'};
}

1;
