rewritten whole code from scratch. need testing.