#!/usr/bin/env ruby
# coding: utf-8

#-------------------------------------------------------------------------------
#
#	キーボード・オン・キーボード
#

require 'curses'
require 'socket'

#---------------------------------------------------------------
#
#	キーボード対応テーブル
#
type = 'FKB8579'; esc = "\e"; tab = "\t"; bs = "\x7f"
table = Hash[													esc,['Esc','A#3'],
											tab,['Tab','B3' ],	?1, ['1',  '   '],
											?q, ['q',  'C4' ],	?2, ['2',  'C#4'],
						?a, ['a',  '   '],	?w, ['w',  'D4' ],	?3, ['3',  'D#4'],
	?z, ['z',  'F2' ],	?s, ['s',  'F#2'],	?e, ['e',  'E4' ],	?4, ['4',  '   '],
	?x, ['x',  'G2' ],	?d, ['d',  'G#2'],	?r, ['r',  'F4' ],	?5, ['5',  'F#4'],
	?c, ['c',  'A2' ],	?f, ['f',  'A#2'],	?t, ['t',  'G4' ],	?6, ['6',  'G#4'],
	?v, ['v',  'B2' ],	?g, ['g',  '   '],	?y, ['y',  'A4' ],	?7, ['7',  'A#4'],
	?b, ['b',  'C3' ],	?h, ['h',  'C#3'],	?u, ['u',  'B4' ],	?8, ['8',  '   '],
	?n, ['n',  'D3' ],	?j, ['j',  'D#3'],	?i, ['i',  'C5' ],	?9, ['9',  'C#5'],
	?m, ['m',  'E3' ],	?k, ['k',  '   '],	?o, ['o',  'D5' ],	?0, ['0',  'D#5'],
	?,, [',',  'F3' ],	?l, ['l',  'F#3'],	?p, ['p',  'E5' ],	?-, ['-',  '   '],
	?., ['.',  'G3' ],	?;, [';',  'G#3'],	?[, ['[',  'F5' ],	?=, ['=',  'F#5'],
	?/, ['/',  'A3' ],	?', ["'",  'A#3'],	?], [']',  'G5' ],	?\\,['\\', 'G#5'],
											bs, ['BS', 'A5' ],	?`, ['`',  'A#5'],
]
z = ['lowlow', 'low', 'mid1', 'mid2', 'hi', 'hihi']; table.each {|k, v|
	v[2] = (v[1] =~ /([A-G]#?)(\d)/) ? (z[$1 < 'C' ? $2.to_i : $2.to_i - 1] + $1) : nil
}
=begin
table = Hash[													27, ['Esc','2C#'],
											 9, ['Tab','2D' ],	?1, ['1',  '2D#'],
											?q, ['q',  '2E' ],	?2, ['2',  '   '],
						?a, ['a',  '0G#'],	?w, ['w',  '2F' ],	?3, ['3',  '2F#'],
	?z, ['z',  '0A' ],	?s, ['s',  '0A#'],	?e, ['e',  '2G' ],	?4, ['4',  '2G#'],
	?x, ['x',  '0B' ],	?d, ['d',  '   '],	?r, ['r',  '2A' ],	?5, ['5',  '2A#'],
	?c, ['c',  '1C' ],	?f, ['f',  '1C#'],	?t, ['t',  '2B' ],	?6, ['6',  '   '],
	?v, ['v',  '1D' ],	?g, ['g',  '1D#'],	?y, ['y',  '3C' ],	?7, ['7',  '3C#'],
	?b, ['b',  '1E' ],	?h, ['h',  '   '],	?u, ['u',  '3D' ],	?8, ['8',  '3D#'],
	?n, ['n',  '1F' ],	?j, ['j',  '1F#'],	?i, ['i',  '3E' ],	?9, ['9',  '   '],
	?m, ['m',  '1G' ],	?k, ['k',  '1G#'],	?o, ['o',  '3F' ],	?0, ['0',  '3F#'],
	?,, [',',  '1A' ],	?l, ['l',  '1A#'],	?p, ['p',  '3G' ],	?-, ['-',  '3G#'],
	?., ['.',  '1B' ],	?;, [';',  '   '],	?[, ['[',  '3A' ],	?=, ['=',  '3A#'],
	?/, ['/',  '2C' ],	?', ["'",  '2C#'],	?], [']',  '3B' ],	?\\,['\\', '   '],
	?{, ['{',  '2D' ],	?}, ['}',  '2D#'],	13, ['Ent','4C' ],	 8, ['BS', '4C#'],
]
=end

#---------------------------------------------------------------
#
#	画面初期化
#
Curses.init_screen
win = Curses::Window.new(Curses.lines, Curses.cols, 0, 0)
win.clear
win.refresh

#---------------------------------------------------------------
#
#	画面を再描画する特異メソッド追加
#
#		history[ [['z', '0A'], head_ms, line], ... ]
#
def win.draw_notes(history, line)
	self.clear

	l = -1
	history.delete_if {|note|
		y = line - note[2]
		if(y < Curses.lines - 2)

			if(l != note[2])
				self.setpos(y + 1, 0); self << '#'
				self.setpos(y + 2, 0); self << '!'
			end
			l = note[2]

			x = note[1] / 40 + 2								# 40: 横幅の倍率
			if(x < Curses.cols - 2)
				self.setpos(y + 1, x); self << note[0][0]; it = note[0][2] and self << ' <%s>' % it
				self.setpos(y + 2, x); self << note[0][1]
				nil
			else
				true
			end
		else
			true
		end
	}

	self.refresh
end

#---------------------------------------------------------------
#
#	現時刻をミリ秒単位で得る
#
def Time.now_msec
	x = self.now
	x.to_i * 1000 + x.usec / 1000
end

#---------------------------------------------------------------
#
#	メイン処理
#
history = Array.new												# 履歴を保持する配列
now_ms = Time.now_msec; last_ms = now_ms - 99999999
line = 0; head_ms = 0

socket = UDPSocket.new()
socket.connect('localhost', 47624)								# melod-daemon へのポートを開く

loop {
	key = $stdin.getc											# キー入力でブロッキング
	now_ms = Time.now_msec

	socket.send(table[key][1], 0) if(table[key])				# melod-daemon へ音階を送信

	if(key == ?L)												# shift + L で再描画する
		win.draw_notes(history, line)
		next
	end

	if(key == 32 or key == ' ')									# スペースバーで改行
		last_ms = now_ms - 99999999
		next
	end

	if(now_ms - last_ms > 2000)									# 2秒間放置されても改行
		head_ms = now_ms
		line += 3
	end

	if(table[key])
		history.push([table[key], now_ms - head_ms, line])
		win.draw_notes(history, line)
	end

	last_ms = now_ms
}

