' Gutenberg v0.9 - by Geoff Camp (aka goksteroo) 2021
'
' A book reader for text based books from Project Gutenberg's library.
' 59 books plus instructions/Gutenberg User License included
'

cls
load bmp "gutenberg2.bmp",mm.hres/2-120,mm.vres/2-100

option explicit
option default integer
option base 1

#include "books.inc"
pause 3500' so cute graphics can be seen!
framebuffer create 1600,1200' 1,920,000 bytes for loading book files

dim pp$' string for building line of text
dim b_num' selected book number
dim fntht' font height
dim wide' adjusted page width
dim b_page' calculated page jump value
dim i,j,x' general counters
dim xit' exit routine flag
dim fg(16),bg(16)' fore/back ground colours
dim col_cnt' colour scheme count
dim b_cnt' book counter for list pages
dim menu' book list number
dim zz' temp char count holder
dim hp' horizontal pos for print
dim vp' vertical pos for print
dim s$' string of spaces
dim nxt_page' flag in print routine
dim old_page' tracks beginning of displayed page
dim chr_cnt' Charcacter count when printing
dim p' for peeking books
dim lin_count' line count
dim turn' flag in print routine
dim f$' string for header/footer
dim old_b' old book number when moving through list of books
dim b_length' length of current book - usewd to calculate % read
dim om' booklist selection flag

fntht=mm.info(fontheight)
if fntht=8 then fntht=10 ' font 7 is 8 pixels high but normal print uses 10 pixel spacing

const fntwid=mm.info(fontwidth)
const widest=int(mm.hres/fntwid)' maximum characteras per line
const lines=(mm.vres-24)/fntht ' 2 less as need title at top and instructions at bottom
const b_add=mm.info(framebuffer)
const p_perc=0.7' percent of page jumping forward/back
const hf_size=mm.hres/8' width of font 1 used in header/footer
const old_mode=int(mm.info(mode))

mode old_mode,8 'force to 8 bit mode - probably not needed, but.....

wide=widest' character positions per line - can be adjusted
b_page=(wide*lines)*p_perc' calculate skip size

'initialise book list, authors, file names, last read postion

restore Books
read b_num

dim b_name$(b_num),b_author$(b_num),b_file$(b_num),b_pos(b_num)

open "books.dat" for input as #1
for i=1 to b_num
  input #1,b_pos(i)
  read b_name$(i),b_author$(i),b_file$(i)
  b_file$(i)=b_file$(i)+".txt"
next i
close #1

' set colour schemes

fg(1)=mm.info(fcolour) 'preserve current fg/bg colours
bg(1)=mm.info(bcolour)
restore col_schemes ' read & set colour schemes
for i=2 to 16
  read bg(i),fg(i)
next i
col_cnt=1 ' colour scheme count
b_cnt=1 ' current selected book

'main loop starts here

do
  menu=1 ' book list page 1-3 = 60 books (57+3xinstructions)
  booklist
  choosebook
  rd_book
    do while keydown(0)>0'clear keys
    loop
loop

end

'print routine

sub rd_book

cls
header b_name$(b_num)+" - "+b_author$(b_num),1
footer "SPC-turn page "+chr$(144)+"-skip up/down "+chr$(145)+"-colours HOME-restart S-save Q-quit"
chr_cnt=b_pos(b_num)
xit=0
print @(0,12);
lin_count=1
pp$=""
zz=chr_cnt ' print count from beginning of current page
hp=(int((widest-wide)/2)*fntwid)
s$=space$(int(widest-wide)/2)
nxt_page=0
do
  if lin_count=1 and nxt_page=0 then old_page=chr_cnt
  nxt_page=1
  p=peek(byte b_add+chr_cnt)
  vp=mm.info(vpos)
  select case p
    case 0' end of book - wait for Q then quit
      xit=1
      print @(hp,vp)pp$;
      footer "End of book - Press [Q] to continue"
      do
      f$=ucase$(inkey$)      
      loop until f$="Q"
    case 32 to 127
      pp$=pp$+chr$(p)
    case 13
      print @(hp,vp)pp$
      pp$=""
      inc chr_cnt
      inc lin_count
  end select
  if len(pp$)>wide then 
    truncate
    print @(hp,vp)pp$
    pp$=""
  end if
  inc chr_cnt
  if lin_count>lines then
    nxt_page=0
    text 0,mm.vres-10," "+str$(int(zz*100/b_length))+"% ",l,7,,map(0),map(1)
    zz=chr_cnt
    turn=0  
    do
      j=keydown(1)
      select case j
        case 32 ' next page
          turn=1
         case 128,136' jump back
         inc old_page,-b_page
         if old_page<0 then old_page=0
          chr_cnt=old_page
          turn=1
        case 129,137' jump forward
          inc old_page,b_page
          if old_page=>b_length then inc old_page,-b_page
          chr_cnt=old_page
          turn=1
        case 83,115' save position
          b_pos(b_num)=old_page
          open "books.dat" for output as #1
          for i=1 to 60
            print #1,b_pos(i)
          next i
          header b_name$(b_num)+" - "+b_author$(b_num),1
        close #1
        case 130
          inc col_cnt,-1
          new_scheme
          pause 100
        case 131
          inc col_cnt
          new_scheme
          pause 100
        case 113,81' quit to menu
          turn=1
          xit=1
        case 49 to 57' 1-9 change width to 10-90%
          wide=widest*((j-48)/10)
          turn=1
          s$=space$(int(widest-wide)/2)
          chr_cnt=old_page
          b_page=(wide*lines)*p_perc
          hp=(int((widest-wide)/2)*fntwid)
          box 0,12,mm.hres,mm.vres-24,0,map(0),map(0)
        case 48 '0 change width to 100%
          wide=widest
          turn=1
          s$=space$(int(widest-wide)/2)
          chr_cnt=old_page
          b_page=(wide*lines)*p_perc
          hp=0
        case 134' home - begin again
          chr_cnt=0
          old_page=0
          turn=1
          text 0,mm.vres-10,"0% ",l,7,,map(0),map(1)
       end select

    loop until turn=1
  box hp,12,wide*fntwid,mm.vres-24,0,map(0),map(0)
  print @(0,12);
  lin_count=1
if j=32 then pause 150 ' next page so prevent several pages turning
endif

loop until xit=1

end sub

' fit text to screen width

sub truncate

  x=len(pp$)
    do
      inc x,-1
      inc chr_cnt,-1
      if x=0 then ' line is longer than wide and no spaces found
        x=int(wide/2)
        chr_cnt=chr_cnt+x
        exit do
      endif
    loop until mid$(pp$,x,1)=" "
  pp$=left$(pp$,x)
  inc lin_count

end sub

' Printbook list

sub booklist

map(0)=bg(col_cnt)
map(1)=fg(col_cnt)
map set
colour map(1),map(0)
cls map(0)
header "Gutenberg Books - G Camp 2021",0
text 20,12,"Book List #"+str$(menu),l,,,map(1),map(0)
for i=1 to 20
  b_num=(menu-1)*20+i
  text 0,(i)*fntht+12,b_name$(b_num)+" - "+b_author$(b_num),l,,,map(1),map(0)
next i
b_num=(menu-1)*20+1
b_cnt=1
text 0,b_cnt*fntht+12,b_name$(b_num)+" - "+b_author$(b_num),l,,,map(0),map(1)
footer chr$(144)+" select book SPC read 1-3 book list "+chr$(145)+" colours Q quit"

end sub

' Select book

sub choosebook

do
  om=menu
  pause 100' Menu delay
  old_b=b_cnt
  j=keydown(1)
  select case j
    case 128,136
      inc b_cnt,-1
      if b_cnt<1 then b_cnt=20
    case 129,137
      inc b_cnt
          if b_cnt>20 then b_cnt=1
    case 130
      inc col_cnt,-1
      new_scheme
    case 131
      inc col_cnt
      new_scheme
    case 49 to 51' 1-3
      menu=j-48
    case 113,81' cheap and dirty exit
      cls
      end 
  end select
  if om<>menu then booklist  
  b_num=(menu-1)*20+b_cnt
  if b_cnt<>old_b then
    text 0,b_cnt*fntht+12,b_name$(b_num)+" - "+b_author$(b_num),l,,,map(0),map(1)
    text 0,old_b*fntht+12,b_name$((menu-1)*20+old_b)+" - "+b_author$((menu-1)*20+old_b),l,,,map(1),map(0)
  end if

loop until j=32

page write framebuffer
cls
page write 0
b_length=mm.info(filesize b_file$(b_num))
load data b_file$(b_num),b_add

end sub

' print header - add "save" if j=1

sub header z$,j' centred, adjusts for font/mode

f$=space$(80)+z$+space$(80)
f$=mid$(f$,len(f$)/2-hf_size/2,hf_size)
text mm.hres/2,0,f$,c,1,,map(0),map(1)
if j=1 then
  text mm.hres,2,"Save:"+str$(b_pos(b_num))+" ",r,7,,map(0),map(1)
endif

end sub

' print footer routine - centred, adjusts for font/mode

sub footer z$

f$=space$(80)+z$+space$(80)
f$=mid$(f$,len(f$)/2-hf_size/2,hf_size)
text mm.hres/2,mm.vres-12,f$,c,1,,map(0),map(1)

end sub

' change colours during menu or book reading

sub new_scheme

if col_cnt=0 then col_cnt=16
if col_cnt=17 then col_cnt=1
map(0)=bg(col_cnt)
map(1)=fg(col_cnt)
map set

end sub

' colour schemes

col_schemes:

data rgb(black),rgb(cyan)
data rgb(black),rgb(green)
data rgb(black),rgb(lightgrey)
data rgb(black),rgb(orange)
data rgb(black),rgb(white)
data rgb(blue),rgb(cyan)
data rgb(blue),rgb(gold)
data rgb(blue),rgb(salmon)
data rgb(80,10,10),rgb(beige)
data rgb(80,10,10),rgb(yellow)
data rgb(grey),rgb(pink)
data rgb(grey),rgb(white)
data rgb(lightgrey),rgb(black)
data rgb(lightgrey),rgb(blue)
data rgb(white),rgb(black)

