package mydbpack1;


$HEADER_SIZE = 256;		#レコード定義256bytes
$MAX_ROW_DIGIT = 5;		#5:10万件(0~99999)で十分だろう・・・
$NOTHING = "NOTHING";
$INFO = "";
$NONE = "NONE";


# コンストラクタ
sub new{
	my $this = shift;     #arg[0]
	my $filename = shift; #arg[1]
	my $fh = shift;       #arg[2]
	my $hash= { "filename" => $filename  #インスタンス変数
			   ,"fh"       => $fh        #インスタンス変数
	};

	if (-f $filename) {
		#ファイルが存在した場合
		open($fh, "+<".$filename);
	} else {
		#ファイルがない場合(作成)
		open($fh, "+>".$filename);
	}

	return bless $hash, $this;
}

#----------------------------------------------------------#
# 排他制御をするので先に明示的にcloseする必要がある
#----------------------------------------------------------#
sub close{
	my $this = shift;
	close($this->{fh});
}

# デストラクタ
#sub DESTROY{
#	my $this = shift;
#	close($this->{delfh});
#	print "file close:DEL_".$this->{filename}."\n";
#	close($this->{fh});
#	print "file close:".$this->{filename}."\n";
#}


;#----------------------------------------------------------#
;# テーブル作成
;# my $tbl = mydbpack1->new("data.tbl", *FH);
;# $tbl->create("NAME,8,VALUE,8,DUMMY,32");
;#----------------------------------------------------------#
sub create() {
	my $this = shift;
	my $cols = shift;
	my $fh = $this->{fh};

	$INFO = $NONE;

	#初めにはROWNUMが自動で入る
	$cols = "ROWNUM,$MAX_ROW_DIGIT,".$cols;

	my($i) = 0;
	my(@tmp) = split(/,/, $cols);
	my($recordsize) = 0;
	my($data) = "";
	for($i=0; $i<scalar(@tmp)/2; $i++) {
		$define[$i] = $tmp[$i*2];
		$length[$i] = $tmp[$i*2+1] + 1;#カンマの分のサイズを加算
		$recordsize = $recordsize + $length[$i];
		$data .= "$define[$i],$length[$i]";
		if ($i != scalar(@tmp)/2-1) {
			$data .= ",";
		}
		
		#ERROR----予約語
		if (($i>0) && ($define[$i] eq "ROWNUM")) {
			$INFO = "CREATE001[ROWNUM_UNUSE]".$define[$i];
			return;
		}
		if ($define[$i] eq "RS") {
			$INFO = "CREATE002[RS_UNUSE]".$define[$i];
			return;
		}
		#----
	}

	#合計サイズ追加
	$data = $data.",RS,$recordsize";

	#initスペース埋め
	$data = sprintf("%-".$HEADER_SIZE."s", $data); #レコード定義256bytes


	#ERROR----
	if ( length($data) > $HEADER_SIZE ) {
		$INFO = "CREATE003[HEADER_SIZE_OVER]".length($data);
		return;
	}
	#----


	seek($fh, 0, 0);
	truncate($fh, 0);
	print $fh $data;
	seek($fh, $HEADER_SIZE-1, 0);
	print $fh '|';
}


;#----------------------------------------------------------#
;# 行番号を指定したSELECT
;# select("1,3,5") カンマ区切りで複数指定可能
;# 戻り値はハッシュで返す( %ret=select(...); $tmp=$ret{"NAME"}[0] )
;#----------------------------------------------------------#
sub select() {
	my $this = shift;
	my(@rownum) = split(/,/, shift);
	my $fh = $this->{fh};

	$INFO = $NONE;

	#ERROR----
	for ($i=0; $i<scalar(@rownum); $i++) {
		if ($rownum[$i] !~ /\d/) {		#(引数CHECK)配列の中身があるか、数字かどうか
			$INFO = "SELECT001[ROWNUM_NO_NUMBER]".$rownum[$i];
			return;
		}
		if ($rownum[$i] < 0) {
			$INFO = "SELECT002[ROWNUM_MINUS]".$rownum[$i];
			return;
		}
		if ($rownum[$i] != int($rownum[$i])) {
			$INFO = "SELECT003[ROWNUM_NO_INTEGER]".$rownum[$i];
			return;
		}
	}
	#----

	my($i) = 0;
	my($buf) = '';
	my(@tmp) = ();
	my(@define) = ();
	my(@length) = ();
    my(%ret) = ();


	seek($fh, 0, 0);
	read($fh, $buf, $HEADER_SIZE-1);#最後の'|'は除く(-1)
	$buf =~ s/ //g;#' '削除
	@tmp = split(/,/, $buf);

	for($i=0; $i<scalar(@tmp)/2; $i++) {
		$define[$i] = $tmp[$i*2];
		$length[$i] = $tmp[$i*2+1];
	}

	my($recsize) = $tmp[scalar(@tmp)-1];
	my($count) = ((-s $fh) - $HEADER_SIZE) / $recsize;

	#ERROR----
	for ($i=0; $i<scalar(@rownum); $i++) {
		if ($rownum[$i] >= $count) {
			$INFO = "SELECT004[ROWNUM_RECORD_OVER]".$rownum[$i];
			return;
		}
	}
	#----

	#SELECT
	for ($i=0; $i<scalar(@rownum); $i++) {
		seek($fh, $rownum[$i] * $recsize + $HEADER_SIZE, 0);
		read($fh, $buf, $recsize-1);#最後の'|'は除く(-1)
		my(@svalue) = split(/,/, $buf);
		for ($j=0; $j<scalar(@define); $j++) {
			$ret{"$define[$j]"}[$i] = $svalue[$j];
		}
	}

	return %ret;
}

;#----------------------------------------------------------#
;#
;# search(name, value);
;#
;#----------------------------------------------------------#
sub search() {
	my $this = shift;
	my($set) = shift;
	my $fh = $this->{fh};

	$INFO = $NONE;

	($uname, $uvalue) = split(/=/, $set, 2);


	my($i) = 0;
	my($buf) = '';
	my(@tmp) = ();
	my(@define) = ();
	my(@length) = ();
    my(%ret) = ();


	seek($fh, 0, 0);
	read($fh, $buf, $HEADER_SIZE-1);#最後の'|'は除く(-1)
	$buf =~ s/ //g;#' '削除
	@tmp = split(/,/, $buf);

	for($i=0; $i<scalar(@tmp)/2; $i++) {
		$define[$i] = $tmp[$i*2];
		$length[$i] = $tmp[$i*2+1];
	}

	my($recsize) = $tmp[scalar(@tmp)-1];
	my($count) = ((-s $fh) - $HEADER_SIZE) / $recsize;

	my(%index) = ();
	my(%size) = ();
	$index{"$define[0]"} =0;
	$size{"$define[0]"} = $length[0];
	for ($i=1;$i<scalar(@define);$i++) {
		$index{"$define[$i]"} = $index{"$define[$i-1]"} + $length[$i-1];
		$size{"$define[$i]"} = $length[$i];
	}

	#ERROR----
	if (($uname ne "ROWNUM") && ($index{"$uname"} == 0)) {
		$INFO = "SEARCH001[NO_COLUMN_NAME]".$uname;
		return;
	}
	#----

	$uvalue = sprintf("%-".($size{"$uname"}-1)."s", $uvalue);

	#SEARCH
	for ($i=0; $i<$count; $i++) {
		seek($fh, $i * $recsize + $index{"$uname"}+ $HEADER_SIZE, 0);
		read($fh, $buf, $size{"$uname"} - 1); #カンマは除く
		if ($buf eq "$uvalue") {
			return $i;
		}
	}

	return $NOTHING;
}

;#----------------------------------------------------------#
;# insert("NAME=AAA,VALUE=103")
;#----------------------------------------------------------#
sub insert() {
	my $this = shift;
	my $set = shift;
	my $fh = $this->{fh};

	$INFO = $NONE;

	my(@iname) = ();
	my(@ivalue) = ();
	my(@tmp) = split(/,/, $set);

	my($i) = 0;
	$iname[0]  = "ROWNUM";
	for ($i=0; $i<scalar(@tmp); $i++) {
		($iname[$i+1], $ivalue[$i+1]) = split(/=/, $tmp[$i], 2);
	}

	my($buf) = '';
	my(@tmp) = ();
	my($recsize) = 0;
	my($ret) = -1;


	seek($fh, 0, 0);
	read($fh, $buf, $HEADER_SIZE-1);#最後の'|'は除く(-1)
	$buf =~ s/ //g;#' '削除
	@tmp = split(/,/, $buf);

	my(@define) = ();
	for($i=0; $i<scalar(@tmp)/2; $i++) {
		$define[$i] = $tmp[$i*2];
		$length[$i] = $tmp[$i*2+1];
	}
	
	my($recsize) = $length[scalar(@tmp)/2-1];
	my($count) = ((-s $fh) - $HEADER_SIZE) / $recsize;

	my(%index) = ();
	my(%size) = ();
	$index{"$define[0]"} =0;
	$size{"$define[0]"} = $length[0];
	for ($i=1;$i<scalar(@define);$i++) {
		$index{"$define[$i]"} = $index{"$define[$i-1]"} + $length[$i-1];
		$size{"$define[$i]"} = $length[$i];
	}

	#ERROR----
	for ($i=0; $i<scalar(@iname); $i++) {
		if (($iname[$i] ne "ROWNUM") && ($index{"$iname[$i]"} == 0)) {
			$INFO = "INSERT001[NO_COLUMN_NAME]".$iname[$i];
			return;
		}
	}
	#ERROR----
	if ( length($count) > $MAX_ROW_DIGIT ) {
		$INFO = "INSERT002[MAX_ROW]".$count;
		return;
	}

	#INSERT
	$ivalue[0] = $count;

	$pos = (-s $fh);

	seek($fh, $pos, 0);
	print $fh sprintf("%-".$recsize."s", "");

	for ($i=0; $i<scalar(@iname); $i++) {
		seek($fh, $pos + $index{"$iname[$i]"}, 0);
		$ivalue[$i] = sprintf( "%-".($size{"$iname[$i]"}-1)."s", substr($ivalue[$i], 0, ($size{"$iname[$i]"}-1)) );
		print $fh $ivalue[$i].",";
	}
	seek($fh, $pos + $recsize - 1, 0);
	print $fh "|";
	$ret = ($pos - $HEADER_SIZE) / $recsize;

	return $ret;
}

;#----------------------------------------------------------#
;# 指定行[@rownum]の複数の指定列を更新する
;# update("NAME=AAA,VALUE=103", "1,3,5")
;#----------------------------------------------------------#
sub update() {
	my $this = shift;
	my($set) = shift;
	my(@rownum) = split(/,/, shift);
	my $fh = $this->{fh};

	$INFO = $NONE;

	my($i) = 0;
	#ERROR----
	for ($i=0; $i<scalar(@rownum); $i++) {
		if ($rownum[$i] !~ /\d/) {		#配列の中身があるか、数字かどうか
			$INFO = "UPDATE001[ROWNUM_NO_NUMBER]".$rownum[$i];
			return;
		}
		if ($rownum[$i] < 0) {
			$INFO = "UPDATE002[ROWNUM_MINUS]".$rownum[$i];
			return;
		}
		if ($rownum[$i] != int($rownum[$i])) {
			$INFO = "UPDATE003[ROWNUM_NO_INTEGER]".$rownum[$i];
			return;
		}
	}
	#----

	my(@uname) = ();
	my(@uvalue) = ();
	my(@uset) = split(/,/, $set);

	my($i) = 0;
	for ($i=0; $i<scalar(@uset); $i++) {
		($uname[$i], $uvalue[$i]) = split(/=/, $uset[$i], 2);
	}


	seek($fh, 0, 0);
	read($fh, $buf, $HEADER_SIZE-1);#最後の'|'は除く(-1)
	$buf =~ s/ //g;#' '削除
	@tmp = split(/,/, $buf);

	my(@define) = ();
	for($i=0; $i<scalar(@tmp)/2; $i++) {
		$define[$i] = $tmp[$i*2];
		$length[$i] = $tmp[$i*2+1];
	}
	
	my($recsize) = $tmp[scalar(@tmp)-1];
	my($count) = ((-s $fh) - $HEADER_SIZE) / $recsize;

	my(%index) = ();
	my(%size) = ();
	$index{"$define[0]"} =0;
	$size{"$define[0]"} = $length[0];
	for ($i=1;$i<scalar(@define);$i++) {
		$index{"$define[$i]"} = $index{"$define[$i-1]"} + $length[$i-1];
		$size{"$define[$i]"} = $length[$i];
	}

	#ERROR----
	for ($j=0; $j<scalar(@uset); $j++) {
		if (($uname[$j] ne "ROWNUM") && ($index{"$uname[$j]"} == 0)) {
			$INFO = "UPDATE004[NO_COLUMN_NAME]".$uname[$j];
			return;
		}
	}
	#ERROR----
	for ($i=0; $i<scalar(@rownum); $i++) {
		if ($rownum[$i] >= $count) {
			$INFO = "UPDATE_005[MAX_ROW]".$rownum[$i];
			return;
		}
	}
	#----

	#UPDATE & SET
	for ($i=0; $i<scalar(@rownum); $i++) {
		for ($j=0; $j<scalar(@uset); $j++) {
			seek($fh, $rownum[$i] * $recsize + $index{"$uname[$j]"} + $HEADER_SIZE, 0);
			$uvalue[$j] = sprintf( "%-".($size{"$uname[$j]"}-1)."s", substr($uvalue[$j], 0, ($size{"$uname[$j]"}-1)) );
			print $fh $uvalue[$j];
		}
	}

}

;#----------------------------------------------------------#
;# レコード件数を返す
;# count()
;#----------------------------------------------------------#
sub count() {
	my $this = shift;
	my $fh = $this->{fh};

	$INFO = $NONE;

	my($i) = 0;
	my($buf) = '';
	my(@tmp) = ();

	my($count) = -1;


	seek($fh, 0, 0);
	read($fh, $buf, $HEADER_SIZE-1);#最後の'|'は除く(-1)
	$buf =~ s/ //g;#' '削除
	@tmp = split(/,/, $buf);


	my($recsize) = $tmp[scalar(@tmp)-1];
	my($count) = ((-s $fh) - $HEADER_SIZE) / $recsize;


	return $count;
}

1;
