#!/usr/bin/ruby
# frozen_string_literal: true
# BSD 2-Clause License
# 
# Copyright (c) 2023, Gregory Cohen
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice, this
   # list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright notice,
   # this list of conditions and the following disclaimer in the documentation
   # and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Get rid of the "ca"s if you want to make it work for the US.
# This program is for personal, non-commerical use.
# Any sites this program downloads from are being used for personal, non-commerical use.
# If any legal problems do arise, feel free to contact me at my
# email and I can modify this program to use some other site.


def puts *a; end

LANG = 'us'
# LANG='ca'
LANG2 = 'en-us'
# LANG2='en-ca'
LANG3 = 'usa'
# LANG3='canada'

begin
  require 'puppeteer-ruby'
  require 'gtk3'
rescue Object
  puts 'Installing puppeteer-ruby and gtk3'
  system 'gem install puppeteer-ruby puppeteer gtk3 || sudo gem install puppeteer-ruby puppeteer gtk3'
end

def exit
  fade($window, 1, 0) do
    Gtk.main_quit
    exit! 0
  end
end

class String
  def button(&block)
    text = self
    button = Gtk::Button.new text, use_underline: true
    button.add_event_listener 'clicked', &block

    $title.add button

    button
  end
end

# $title.add(

class KillOther
  def initialize
    a = `pidof ruby`.split
    (a - [Process.pid.to_s]).each { |i| `kill #{i} -9` }
  end; end
KillOther.new

def browser(headless = false)
  Puppeteer.launch(headless: headless,
                   ignore_default_args: ['--enable-blink-features=IdleDetection', '--enable-automation'], slow_mo: 0, args: ['--window-size=1280,800']) do |i|
    i.pages.first.viewport = Puppeteer::Viewport.new(width: 1280, height: 800)

    yield i
  end
end

def curl(*args)
  #  new = 'TSLA|AAPL|'
  new = 'TSLA,AAPL,GME,'
  #      args.each do |arg|
  args.each do |arg|
    words = case LANG
            when /^ca$/i
              %w[.TO .CN .V]
            else
              ['']
            end
    words.each do |n|
      new += [arg, n, ','].join; end
  end
  #      end

  new = new.chomp ','

  a = "https://query1.finance.yahoo.com/v7/finance/quote?lang=#{LANG2}&region=#{LANG}&corsDomain=finance.yahoo.com&fields=symbol,regularMarketPrice&symbols=#{new}"
  '

#      			pts a

puts a '
  puts a
  a = `curl -s '#{a}' 2>/dev/null`.strip
end

def watch
  Thread.new do |_i|
    #  sleep 1
    sleep 5
    #  	sleep 3
    loop do
      next if $args.nil?

      # puts 'Curling'
      def unused
        a = "https://query1.finance.yahoo.com/v7/finance/quote?lang=en-#{LANG}&region=#{LANG}&corsDomain=finance.yahoo.com&fields=symbol,marketState,regularMarketPrice,regularMarketChange,regularMarketChangePercent,preMarketPrice,preMarketChange,preMarketChangePercent,postMarketPrice,postMarketChange,postMarketChangePercent&symbols=#{new.chomp ','}"
      end

      a = curl(*$args)
      #      			puts a
      #     			exit! 0
      text = ''
      next if a.empty?

      a = JSON.parse(a)

      a = a['quoteResponse']

      result = a['result']
      msgs = ''
      x = 0
      symbols = result.map do |i|
        i['symbol']
      end
      $symbols = symbols

      #      puts results.in
      # lts.inspect
      #	max = symo

      max = (result.map do |i|
        i['symbol'].size
        #		i["sym
      end).max
      result.each do |i|
        #      	if
        # puts i
        market = i['market'].gsub '_market', ''
        next unless market.downcase.strip == LANG

        true

        market = i['market']
        p = i['regularMarketPrice']
        symbol = i['symbol']
        mx = symbol.size
        mx = max - mx
        '
        msgs += "}\n"
        '
        # size=max
        msgs += "<span>#{symbol}</span>  " + (' ' * mx) + "<span color='green'>#{p}</span>\n"
        #        msgs += "</span>"
        #        msgs += " " * 5
        #        msgs
        #        msgs += "\n" if (x % 5).zero?

        x += 1
      end; Thread.new do |_i|
             $tv2&.set "<i>Hot stocks and 'Strong Buys' for <b>#{get_time}</b></i>\n\n\n#{msgs}"
           end
    end
  end
end

require 'json'
require 'shellwords'
watch

time = Time.now

class TextView < Gtk::TextView
  def initialize(a)
    super()
    set a.to_s

    self.cursor_visible = false
    self.editable = false
    self.left_margin = 20

    self.right_margin = 20

    self.top_margin = 20

    self.bottom_margin = 20
  end

  def set(arg)
    arg = '' if arg.nil?
    buffer&.text = ''
    buffer.insert_markup(buffer.start_iter, arg, arg.length + 2)
  end
end

module Gtk
  class Widget
    alias :add_event_listener signal_connect
  end
end

class Button < Gtk::Button
  def initialize(_tv)
    super(label: '_Get current price', use_underline: true)

    add_event_listener('clicked') do |_i|
      def set_text(*args)
        t = "Enter stock | #{args.join ' '}"
        $enter_label.text = (t && t.to_s) || ''
      end

      print $entry.buffer.text.inspect

      stock = $entry.buffer.text.strip.upcase.split&.first
      c = curl(stock)

      unless c.empty?
        #    	value = JSON.parse(c)["quoteResponse"]["
        map = { 'regularMarketPrice' => '(not Canadian stock)'.strip }
        v = begin
          (JSON.parse(c)['quoteResponse']['result'][0] || map)
        rescue StandardError
          map
        end

        set_text stock, v['regularMarketPrice']
      end

      #    c['quoteResponse'
      #	puts c

      #    set_text JSON.parse(c)["quoteResp
      #   c['qu
      #  c
      #    	c =
      #   	$entry.text = ["quoteResponse"]["result"]
    end
  end
end

class EntryBox < Gtk::Box
  def initialize(button)
    super(:horizontal)
    #    $enter_stock = self
    $enter_label = Gtk::Label.new('Enter stock')
    pack_start $enter_label
    #    $button =
    $entry.add_event_listener('activate') do |_i|
      $button.signal_emit 'clicked'
    end

    $button = button
    pack_start($entry, true, true)
  end
end

class Box2 < Gtk::Box
  def initialize(o, *args)
    super(o)
    args.each do |i|
      pack_start i, false
    end
  end
end

begin
end

class Box < Gtk::Box
  def initialize(_a)
    super(:vertical)

    pack_start Scrolled.new $tv, true

    scrolled = Scrolled.new $tv3, true

    box = Box2.new :horizontal, $tv2, scrolled
    pack_start box, true
    button = Button.new($tv)
    pack_start EntryBox.new(button)

    pack_start BB.new(button), true, true, 5
  end
end

class BB < Gtk::Box
  def initialize(a)
    super(:horizontal)
    pack_start(a, true, false)
  end
end

class Scrolled < Gtk::ScrolledWindow
  def initialize(tv, big)
    super()
    add tv
    if big
      set_size_request(330, 200)
      return
    end

    set_size_request(400, 200)
  end
end

$map = { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11, 12 => 12, 13 => 1, 14 => 2, 15 => 3, 16 => 4,
         17 => 5, 18 => 6, 19 => 7, 20 => 8, 21 => 9, 22 => 10, 23 => 11, 24 => 0 }

def get_time
  time = Time.now
  sec = time.sec.to_s
  min = time.min.to_s
  hour  = time.hour

  pm = hour >= 12
  text = if pm
           'pm'
         else
           'am'
         end
  text = text.upcase
  ret = " #{$map[hour]}:0#{min}:0#{sec}#{text}".gsub(/[0-9]([0-9]{2})/, '\\1')
  ret = "0#{ret}" if ret[0] == ':'

  ret
  #  	ret = "
end

def set_time
  $window.title = get_time
end

class String
  def path
    File.expand_path self
  end
end

class OutputWindow < Gtk::Window
  def initialize(window_text)
    super()
    add_event_listener 'destroy' do |_i|
      puts 'Quitting'
      Gtk.main_quit
    end

    $tv = TextView.new(window_text)
    $tv2 = TextView.new(text2 = 'Real time Yahoo stocks will display here')

    $tv3 = TextView.new('')

    add Box.new window_text
    $window = self

    self.title = 'To Buy '
    self.titlebar = title = Gtk::HeaderBar.new
    title.add(a = Gtk::Button.new('See Graphs'))
    a.add_event_listener('clicked') do |_i|
      #    	if test, "d"
      if Dir.exist?('~/Stock Screenshots'.path)
        `caja ~/'Stock Screenshots' &`
      else
        Thread.new do |_i|
          require 'shellwords'
          def show(a, b, n)
            Dir.chdir Dir.home
            require 'fileutils'
            page = b.pages.first
            begin
              page.goto "https://ca.finance.yahoo.com/chart/#{a}"
            rescue StandardError
              retry
            end
            begin
              Dir.chdir 'Stock Screenshots'
            rescue StandardError
              retry
            end
            sleep 5
            begin
              page.screenshot path: "#{n}_#{a}.png"
            rescue StandardError
              retry
            end
          end
          Dir.chdir Dir.home
          require 'fileutils'
          folder = 'Stock Screenshots'

          FileUtils.rm_rf folder
          FileUtils.mkdir_p folder
          `caja #{folder.shellescape}`

          if $symbols
            browser do |b|
              n = 0
              $symbols.each do |i|
                show i, b, n += 1
              end
            end
          end

          Dir.chdir Dir.home
        end
      end
    end
    $title = title
    'Communicate with all people in life'.button do |_i|
      `mate-terminal -e "questions" &`
    end
    $quit_button = 'Quit'.button do |_i|
      exit
    end

    'Open Expedia'.button do |_i|
      `google-chrome expedia.ca &`
    end

    'Houses for Sale'.button do |_i|
      `google-chrome   https://www.google.com/search?q=houses+for+sale&oq=houses+for+sale&aqs=chrome.0.69i59j0i67i457j0i402l2j0i67j0i512l5.1742j0j7&sourceid=chrome&ie=UTF-8`
    end

    title.add(a = Gtk::Button.new('S.shot'))
    a.add_event_listener('clicked') { |_i| system 'mate-screenshot & ' }
    title.add(a = Gtk::Button.new('Transparency'))
    a.add_event_listener('clicked') do |_i|
      $window.set_opacity({ 1.0 => 0.2 }[$window.opacity] || 1.0)
    end
    title.add($monitor_button = a = Gtk::Button.new('Monitor email'))
    a.add_event_listener('clicked') do |_i|
      Thread.new do |_i|
        IO.popen('espeak', 'w') { |i| i.print 'Using espeak for this' }

        loop do
          if `himalaya | grep -F Wea -i`.strip == '' #			else
            $monitor_button.label = "No new transactions as of #{get_time}"
          else
            (IO.popen('espeak', 'w') { |i| i.print 'Money'; })
          end
        end
      end
    end
    realize
    fade(self, 0, 1)

    Thread.new do |_i|
      loop do
        text = gettext

        $tv3.set text[1] #+ "!"
        puts text[1]
        sleep(0.5)
      end
    end

    show_all
    Gtk.main
  end
end

def fade(obj, start, stop)
  return unless ARGV[0] != '-n'

  Thread.new do |i|
    i = start.to_f
    loop do
      obj.set_opacity i += ((stop - start) / 100.0)
      sleep 0.0005
      if (stop - start).positive?
        break if i >= stop; elsif i <= stop
                              break
      end
    end
    yield if block_given?
  end
end

def site
  "https://www.tradingview.com/markets/stocks-#{LANG3}/market-movers-most-volatile/"
end
Warning[:deprecated] = false
require 'fileutils'
def gettext
  a = [nil]

  set_time

  browser(headless: 1) do |browser|
    page = browser.pages[0]
    page.goto(site, wait_until: 'domcontentloaded')
    str = <<~EOF
      async function() {
      //return [];
      var list = [];
      var body = document.querySelectorAll("tbody")
      for (var b of body) {
      	b =[... b.childNodes  ];

      for(var i=0; i < b.length;++i) {
      	var text = b.at(i).innerText.trim() || "";
      	var split = text.split("\t");
      	var price = "$" + split?.at(1);
      	var up = split?.at(2);
      	if(text.match(/strong buy/i)) { list.push([b.at(i).querySelector("a").innerText,price,up]); } }
      }

      //alert(list);

      return list;
      }

    EOF

    x = 0
    next_list = []
    a = page.evaluate(str)

    i = 0
    <<EOF
    a = a.select do |f|

      val = f[2][0..-2].to_f
      val2 = f[1][1..-1].to_f

      range = 20.0
      neg_range = -range
      (val <= neg_range || val >= range)
    end; # print"Enter money>  "
EOF

    a = a.sort do |a, b|
      def get(a)
        a[2][0..-2].to_f
      end
      a = get a
      b = get b
      b <=> a

      #      (get b) <=> (get a

      #     get(b) <=> get(a)
    end
    length = a.map do |i|
      i[0].size
    end.max
    $args = a.map { |i| i[0] }
    header = "Most volatile stocks\nas of #{get_time}"
    def markup(i)
      "<span color=#{'blue'.dump}><big><i><b>#{i}</b></i></big></span>\n\n\n\n"
    end

    text = [text, markup(header)]
    a.each do |i|
      text[1] += ([
        i[0] + (' ' * (length - i[0].length)), "<span color=#{'green'.dump}>#{i[2]}</span>"
      ].map do |i|
                    i
                  end).join(' | ') + "\n"
    end

    text[1] = text[1].strip

    text
  end
end

$entry = Gtk::Entry.new
$entry.alignment = 0.5
$entry.text = ''
$entry.placeholder_text = 'SHOP'

$entry.add_event_listener 'key-release-event' do |_i|
  $button.signal_emit 'clicked'
end

provider = Gtk::CssProvider.new
provider.load data: <<~EOF
    window {
    }



    button { font-weight: normal; }
    button {
    	color: #000000; border-radius: 30px; text-shadow: 1px 1px 1px grey; }
    label {
    	text-decoration: underline;
    }

    headerbar {
    margin: 0px;
    padding: 0px;
    border-radius: 0px;
  /*  float: left;*/
    }


    textview {
    font-family: "DejaVu Sans", "Sans";
    /*font-size: 40px;*/
    }
  * > button:last-child {
  	opacity: 0;
  }
    scrollbar {
    	opacity: 0; }

    * {
    	border-bottom-left-radius: 50px;

    	border-bottom-right-radius: 50px;
    		margin: 5px;
    }

    *:focus {
    }

    entry * {
    	margin: 50px;
    	padding: 50px;
    }
    window {
    	box-shadow: 3px 3px 3px red;
    }
EOF
Gtk::StyleContext.add_provider_for_screen(Gdk::Screen.default,
                                          provider,
                                          Gtk::StyleProvider::PRIORITY_APPLICATION)

text = <<~EOF


  <span size="x-large"><i><span color="#0000AA">Swingiest #{LANG3.upcase} stocks possible shown</span></i></span>

  Generate graphs, they will be stored in a folder called &quot;Stock Screenshots&quot;
  * See what is abnormally down, due to a mistake,
  fear or something of the sort. There are a lot of reasons why a company might go down,
  but companies wouldn't want their stock price to be down. Dips are temporary.
  Unless a company is legitimately failing.



  __Swingy stocks aren't failing stocks.__

  Two methodologies

  <i>Buy the most swingy stock for low, then sell for like +50%.This might take some time. Or you could have a quick +20% profit.</i>

  Buy a swingy stock for -5% or so, then sell for 1.1x that, +5%. You could do this daily.


  All listed stocks are the most volatile stocks.

  ALL swingy stock prices are listed, and are updated every 0.5 seconds.


  This would be hard to do if you had a full time job. Unless one automates it, which is possible.

  Program is self-contained and multithreaded, one window maximum.



  Spend your time doing meaningful things, and not being a slave for a sociopathic system.

  Open #{File.expand_path(__FILE__)} and change language settings on top of file to change lang settings.

  Since I am Canadian, default is canada
EOF

text = text.gsub(/\n{2,}/, "\n\n")
OutputWindow.new text.strip
