package taskscaler

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/hashicorp/go-hclog"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"

	"gitlab.com/gitlab-org/fleeting/fleeting"
	dummy "gitlab.com/gitlab-org/fleeting/fleeting/plugin/fleeting-plugin-dummy"
	"gitlab.com/gitlab-org/fleeting/fleeting/provider"
	taskscalermetrics "gitlab.com/gitlab-org/fleeting/taskscaler/mocks/metrics"
)

func TestTaskscalerIdleProvision(t *testing.T) {
	const (
		capacityPerInstance = 2
		idleCount           = 10
		acquireNum          = 1
		expectedInstances   = 6
	)

	group := &dummy.InstanceGroup{MaxSize: 1000}

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	ts, err := New(ctx, group, WithCapacityPerInstance(capacityPerInstance), WithUpdateInterval(time.Second), WithUpdateIntervalWhenExpecting(time.Second))
	require.NoError(t, err)

	ts.ConfigureSchedule(Schedule{
		IdleCount: idleCount,
	})

	for i := 0; i < acquireNum; i++ {
		name := fmt.Sprintf("task_%d", i)
		go ts.Acquire(ctx, name)
		defer ts.Release(name)
	}

	var running int
	for {
		time.Sleep(50 * time.Millisecond)

		if ctx.Err() != nil {
			break
		}

		for _, inst := range group.List() {
			// dummy instance group: move creating instances to running
			if inst.State == provider.StateCreating {
				group.SetInstanceState(inst.ID, provider.StateRunning)
				running++
			}
		}

		if running == expectedInstances {
			break
		}
	}

	assert.Equal(t, expectedInstances, running)
}

func TestReserveUnreserve(t *testing.T) {
	t.Run("reserve when not taking reservations", func(t *testing.T) {
		group := &dummy.InstanceGroup{MaxSize: 1000}
		ts, err := New(context.Background(), group)
		require.NoError(t, err)

		prevAvailable, prevPotential := ts.Capacity()
		assert.ErrorIs(t, ts.Reserve("abc"), ErrNoReservations)
		nextAvailable, nextPotential := ts.Capacity()

		assert.Equal(t, prevAvailable, nextAvailable)
		assert.Equal(t, prevPotential, nextPotential)
	})

	t.Run("reserve and unreserve", func(t *testing.T) {
		group := &dummy.InstanceGroup{MaxSize: 1000}
		ts, err := New(context.Background(), group, WithReservations())
		require.NoError(t, err)

		prevAvailable, prevPotential := ts.Capacity()
		assert.NoError(t, ts.Reserve("abc"))
		nextAvailable, nextPotential := ts.Capacity()

		assert.Equal(t, prevAvailable, nextAvailable)
		assert.Equal(t, prevPotential-1, nextPotential)

		prevAvailable, prevPotential = ts.Capacity()
		ts.Unreserve("abc")
		nextAvailable, nextPotential = ts.Capacity()

		assert.Equal(t, prevAvailable, nextAvailable)
		assert.Equal(t, prevPotential+1, nextPotential)
	})

	t.Run("unreserve unknown", func(t *testing.T) {
		group := &dummy.InstanceGroup{MaxSize: 1000}
		ts, err := New(context.Background(), group, WithReservations())
		require.NoError(t, err)

		prevAvailable, prevPotential := ts.Capacity()
		ts.Unreserve("abc")
		nextAvailable, nextPotential := ts.Capacity()

		assert.Equal(t, prevAvailable, nextAvailable)
		assert.Equal(t, prevPotential, nextPotential)
	})

	t.Run("reserve more than potential capacity", func(t *testing.T) {
		group := &dummy.InstanceGroup{MaxSize: 1000}
		ts, err := New(context.Background(), group, WithReservations())
		require.NoError(t, err)

		_, potential := ts.Capacity()

		for i := 0; i < potential; i++ {
			assert.NoError(t, ts.Reserve(fmt.Sprintf("acq-%d", i)))
		}

		assert.ErrorIs(t, ts.Reserve("one more"), ErrNoCapacity)
	})
}

func TestKeepReservedOnScaleDown(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{MaxSize: 5}
	ts, err := New(context.Background(), group, WithReservations())
	require.NoError(t, err)

	go configureScaleTransitions(ctx, group)

	// scale to 5
	ts.ConfigureSchedule(Schedule{IdleCount: 5})
	waitForIdleCapacity(t, ctx, ts, 5, true)

	// reserve all idle capacity
	for i := 0; i < ts.Schedule().IdleCount; i++ {
		assert.NoError(t, ts.Reserve(fmt.Sprintf("reserve-%d", i)))
	}

	// Remove idle scale
	ts.ConfigureSchedule(Schedule{IdleCount: 0})

	// unreserve two, allow scaling down of two instances, but keep others
	ts.Unreserve("reserve-0")
	ts.Unreserve("reserve-1")

	// taskscaler should only scale down to 3, as the calculation takes into
	// account that we still have a capacity of 3 tasks reserved
	waitForIdleCapacity(t, ctx, ts, 3, false)
	instances := &ts.(*taskscaler).instances
	idle, _, _ := instances.Capacity()
	assert.Equal(t, 3, idle)
}

func TestPreemptiveReserveAcquire(t *testing.T) {
	type (
		capacity struct {
			available int
			potential int
		}

		expected struct {
			success       bool
			beforeReserve capacity
			afterReserve  capacity
			afterAcquire  capacity
		}
	)

	tests := map[string]struct {
		maxInstances         int
		capacityPerInstance  int
		maxUses              int
		idleCount            int
		expectedReservations []expected
	}{
		"idle scaling: 5 idle, 1 tasks per instance": {
			maxInstances:        10,
			capacityPerInstance: 1,
			idleCount:           5,
			expectedReservations: []expected{
				{true, capacity{5, 10}, capacity{4, 9}, capacity{4, 9}},
				{true, capacity{4, 9}, capacity{3, 8}, capacity{3, 8}},
				{true, capacity{3, 8}, capacity{2, 7}, capacity{2, 7}},
				{true, capacity{2, 7}, capacity{1, 6}, capacity{1, 6}},
				{true, capacity{1, 6}, capacity{0, 5}, capacity{0, 5}},
				{false, capacity{0, 5}, capacity{0, 5}, capacity{0, 5}},
			},
		},
		"idle scaling: 5 idle, 2 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 2,
			idleCount:           5,
			expectedReservations: []expected{
				{true, capacity{6, 6}, capacity{5, 5}, capacity{5, 5}},
				{true, capacity{5, 5}, capacity{4, 4}, capacity{4, 4}},
				{true, capacity{4, 4}, capacity{3, 3}, capacity{3, 3}},
				{true, capacity{3, 3}, capacity{2, 2}, capacity{2, 2}},
				{true, capacity{2, 2}, capacity{1, 1}, capacity{1, 1}},
				{true, capacity{1, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
		"idle scaling: 5 idle, 3 max use, 2 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 2,
			maxUses:             3,
			idleCount:           5,
			expectedReservations: []expected{
				{true, capacity{6, 6}, capacity{5, 5}, capacity{5, 5}},
				{true, capacity{5, 5}, capacity{4, 4}, capacity{4, 4}},
				{true, capacity{4, 4}, capacity{3, 3}, capacity{3, 3}},
				{true, capacity{3, 3}, capacity{2, 2}, capacity{2, 2}},
				{true, capacity{2, 2}, capacity{1, 1}, capacity{1, 1}},
				{true, capacity{1, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
		"idle scaling: 3 idle, 1 max use, 1 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 1,
			maxUses:             1,
			idleCount:           3,
			expectedReservations: []expected{
				{true, capacity{3, 3}, capacity{2, 2}, capacity{2, 2}},
				{true, capacity{2, 2}, capacity{1, 1}, capacity{1, 1}},
				{true, capacity{1, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
		"on demand: 3 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 3,
			idleCount:           0,
			expectedReservations: []expected{
				{true, capacity{0, 9}, capacity{0, 8}, capacity{2, 8}},
				{true, capacity{2, 8}, capacity{1, 7}, capacity{1, 7}},
				{true, capacity{1, 7}, capacity{0, 6}, capacity{0, 6}},
				{true, capacity{0, 6}, capacity{0, 5}, capacity{2, 5}},
				{true, capacity{2, 5}, capacity{1, 4}, capacity{1, 4}},
				{true, capacity{1, 4}, capacity{0, 3}, capacity{0, 3}},
				{true, capacity{0, 3}, capacity{0, 2}, capacity{2, 2}},
				{true, capacity{2, 2}, capacity{1, 1}, capacity{1, 1}},
				{true, capacity{1, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
		"on demand: 3 max use, 2 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 2,
			maxUses:             3,
			idleCount:           0,
			expectedReservations: []expected{
				{true, capacity{0, 6}, capacity{0, 5}, capacity{1, 5}},
				{true, capacity{1, 5}, capacity{0, 4}, capacity{0, 4}},
				{true, capacity{0, 4}, capacity{0, 3}, capacity{1, 3}},
				{true, capacity{1, 3}, capacity{0, 2}, capacity{0, 2}},
				{true, capacity{0, 2}, capacity{0, 1}, capacity{1, 1}},
				{true, capacity{1, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
		"on demand: 1 max use, 1 tasks per instance": {
			maxInstances:        3,
			capacityPerInstance: 1,
			maxUses:             1,
			idleCount:           0,
			expectedReservations: []expected{
				{true, capacity{0, 3}, capacity{0, 2}, capacity{0, 2}},
				{true, capacity{0, 2}, capacity{0, 1}, capacity{0, 1}},
				{true, capacity{0, 1}, capacity{0, 0}, capacity{0, 0}},
				{false, capacity{0, 0}, capacity{0, 0}, capacity{0, 0}},
			},
		},
	}

	for tn, tc := range tests {
		t.Run(tn, func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
			defer cancel()

			group := &dummy.InstanceGroup{MaxSize: tc.maxInstances}

			ts, err := New(ctx, group,
				WithCapacityPerInstance(tc.capacityPerInstance),
				WithMaxUseCount(tc.maxUses),
				WithReservations())
			require.NoError(t, err)

			ts.ConfigureSchedule(Schedule{IdleCount: tc.idleCount, PreemptiveMode: tc.idleCount > 0})

			go configureScaleTransitions(ctx, group)
			waitForIdleCapacity(t, ctx, ts, tc.idleCount, true)

			for idx, e := range tc.expectedReservations {
				key := fmt.Sprintf("acq-%d", idx)

				available, potential := ts.Capacity()
				assert.Equal(t, e.beforeReserve, capacity{available, potential}, "before reserve: %d", idx)

				reservedErr := ts.Reserve(key)
				assert.NotErrorIs(t, reservedErr, ErrNoReservations)

				available, potential = ts.Capacity()
				assert.Equal(t, e.afterReserve, capacity{available, potential}, "after reserve: %d", idx)

				if reservedErr == nil {
					_, err := ts.Acquire(ctx, key)
					if e.success {
						assert.NoError(t, err)
					}
				}

				available, potential = ts.Capacity()
				assert.Equal(t, e.afterAcquire, capacity{available, potential}, "after acquire: %d", idx)
			}
		})
	}
}

func TestReservePreemptivePreexisting(t *testing.T) {
	doneCh := make(chan struct{}, 1)
	defer func() { <-doneCh }()

	group := &dummy.InstanceGroup{MaxSize: 1}
	group.AddInstance(provider.StateRunning)
	ts, err := New(context.Background(), group, WithReservations(), WithInstanceUpFunc(func(id string, info provider.ConnectInfo, cause fleeting.Cause) (keys []string, used int, err error) {
		doneCh <- struct{}{}
		return nil, 0, assert.AnError
	}))
	require.NoError(t, err)
	defer ts.Shutdown(context.Background())

	// In preemptive mode, a running instance, that isn't ready shouldn't be reservable
	ts.ConfigureSchedule(Schedule{IdleCount: 1, PreemptiveMode: true})
	require.Error(t, ts.Reserve("reserve-0"))
}

func TestTransitionIdleCount(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{}

	ts, err := New(ctx, group)
	require.NoError(t, err)

	go configureScaleTransitions(ctx, group)

	last := 0
	for _, idle := range []int{5, 4, 1, 0, 5, 10, 0} {
		scaleUp := idle > last
		last = idle

		ts.ConfigureSchedule(Schedule{IdleCount: idle})
		waitForIdleCapacity(t, ctx, ts, idle, scaleUp)
	}
}

func TestAcquireGetRelease(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{MaxSize: 5}

	ts, err := New(ctx, group, WithUpdateInterval(time.Second), WithUpdateIntervalWhenExpecting(time.Second))
	require.NoError(t, err)

	go configureScaleTransitions(ctx, group)

	require.Nil(t, ts.Get("unknown"))

	acq, err := ts.Acquire(ctx, "hello")
	require.NoError(t, err)

	require.Equal(t, acq, ts.Get("hello"))

	ts.Release("hello")
	require.Nil(t, ts.Get("hello"))
}

func TestAcquireTimeout(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{MaxSize: 1}

	ts, err := New(ctx, group, WithUpdateInterval(time.Second), WithUpdateIntervalWhenExpecting(time.Second))
	require.NoError(t, err)

	go configureScaleTransitions(ctx, group)

	_, err = ts.Acquire(ctx, "should-acquire")
	require.NoError(t, err)

	ctx, cancel = context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	_, err = ts.Acquire(ctx, "should-timeout")
	require.ErrorIs(t, err, context.DeadlineExceeded)
}

func TestRemoveAllInstances(t *testing.T) {
	tests := map[string]struct{ active, idle int }{
		"all idle removed":                     {idle: 2},
		"couldn't remove active":               {active: 2},
		"removed idle, couldn't remove active": {idle: 3, active: 1},
	}

	for tn, tc := range tests {
		t.Run(tn, func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()

			group := &dummy.InstanceGroup{MaxSize: tc.active + tc.idle}

			buf := new(bytes.Buffer)
			var w io.Writer = buf
			if testing.Verbose() {
				w = io.MultiWriter(w, os.Stdout)
			}

			logger := hclog.New(&hclog.LoggerOptions{
				Output: w,
			})

			defer func() {
				assert.Equal(t, tc.active, strings.Count(buf.String(), "instance couldn't be removed"), "couldn't remove active")
				assert.Equal(t, tc.idle, strings.Count(buf.String(), "instance removed"), "remove idle")
				assert.Contains(t, buf.String(), "taskscaler's shutdown completed")
			}()

			ts, err := New(ctx, group, WithUpdateInterval(time.Second), WithUpdateIntervalWhenExpecting(time.Second), WithDeleteInstancesOnShutdown(), WithLogger(logger))
			require.NoError(t, err)

			go configureScaleTransitions(ctx, group)

			ts.ConfigureSchedule(Schedule{IdleCount: tc.idle})

			waitForIdleCapacity(t, ctx, ts, tc.idle, true)

			for i := 0; i < tc.active; i++ {
				_, err = ts.Acquire(ctx, fmt.Sprintf("active-%d", i))
				assert.NoError(t, err)
			}

			ts.Shutdown(ctx)
		})
	}
}

func TestWithMetricsCollection(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{MaxSize: 20}

	buf := new(bytes.Buffer)
	var w io.Writer = buf
	if testing.Verbose() {
		w = io.MultiWriter(w, os.Stdout)
	}

	logger := hclog.New(&hclog.LoggerOptions{
		Output: w,
	})

	tsMetrics := taskscalermetrics.NewCollector(t)

	maxN := func(max int) any {
		return mock.MatchedBy(func(n int) bool {
			return n >= 0 && n <= max
		})
	}

	tsMetrics.EXPECT().MaxUseCountSet(0)
	tsMetrics.EXPECT().MaxTasksPerInstanceSet(1)
	tsMetrics.EXPECT().TasksCountSet(mock.Anything, maxN(2))
	tsMetrics.EXPECT().TaskOperationInc(mock.Anything)
	tsMetrics.EXPECT().DesiredInstancesSet(maxN(4))
	tsMetrics.EXPECT().ScaleOperationInc("up")
	tsMetrics.EXPECT().TaskInstanceReadinessTimeObserve(mock.Anything)

	ts, err := New(
		ctx,
		group,
		WithUpdateInterval(time.Second),
		WithUpdateIntervalWhenExpecting(time.Second),
		WithLogger(logger),
		WithMetricsCollector(tsMetrics),
	)
	require.NoError(t, err)
	defer ts.Shutdown(ctx)

	go configureScaleTransitions(ctx, group)

	ts.ConfigureSchedule(Schedule{IdleCount: 2})
	waitForIdleCapacity(t, ctx, ts, 2, true)

	for i := 0; i < 2; i++ {
		_, err = ts.Acquire(ctx, fmt.Sprintf("instance-%d", i))
		assert.NoError(t, err)
	}
}

func TestStableInstances(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	defer cancel()

	const (
		stableInstances     = 2
		capacityPerInstance = 2
		workers             = stableInstances * capacityPerInstance * 2
	)

	group := &dummy.InstanceGroup{MaxSize: stableInstances}

	buf := new(bytes.Buffer)
	var w io.Writer = buf
	if testing.Verbose() {
		w = io.MultiWriter(w, os.Stdout)
	}

	logger := hclog.New(&hclog.LoggerOptions{
		Output: w,
	})

	ts, err := New(ctx, group, WithUpdateInterval(time.Second), WithUpdateIntervalWhenExpecting(time.Second), WithLogger(logger), WithCapacityPerInstance(capacityPerInstance))
	require.NoError(t, err)
	defer ts.Shutdown(ctx)

	go configureScaleTransitions(ctx, group)

	ts.ConfigureSchedule(Schedule{IdleCount: stableInstances, IdleTime: 2 * time.Minute})

	waitForIdleCapacity(t, ctx, ts, stableInstances, true)

	limit := make(chan struct{}, workers)
	for i := 0; i < workers; i++ {
		limit <- struct{}{}
	}

	// for 30 seconds, constantly acquire and release as much as we can
	var (
		unique = make(map[string]struct{})
		mu     = new(sync.Mutex)
		until  = time.Now().Add(30 * time.Second)
	)
	for i := 0; time.Now().Before(until); i++ {
		<-limit

		go func(key string) {
			acq, err := ts.Acquire(ctx, key)
			require.NoError(t, err)

			// wait 3 seconds before releasing the hold on the slot
			time.Sleep(3 * time.Second)
			ts.Release(key)

			t.Logf("acquired and released %s", key)

			mu.Lock()
			unique[acq.InstanceID()] = struct{}{}
			mu.Unlock()

			limit <- struct{}{}
		}(fmt.Sprintf("job-%d", i))
	}

	for i := 0; i < workers; i++ {
		<-limit
	}

	// for 30 seconds, don't do anything
	time.Sleep(30 * time.Second)

	mu.Lock()
	defer mu.Unlock()

	// the expectation is that even though we tried to acquire beyond the capacity we're configured for,
	// we didn't exceed the max instances allowed and the idle scale rule didn't scale up nor down during
	// moments of activity and inactivity
	assert.Equal(t, stableInstances, len(unique))
	assert.Equal(t, stableInstances, len(group.List()))
	assert.Equal(t, stableInstances, strings.Count(buf.String(), "instance discovery:"), "instance discovery")
}

func TestPotentialCapacityWithOutOfBandScaling(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	group := &dummy.InstanceGroup{MaxSize: 5}

	buf := new(bytes.Buffer)
	var w io.Writer = buf
	if testing.Verbose() {
		w = io.MultiWriter(w, os.Stdout)
	}

	logger := hclog.New(&hclog.LoggerOptions{
		Output: w,
	})

	ts, err := New(ctx, group,
		WithUpdateInterval(time.Second),
		WithUpdateIntervalWhenExpecting(time.Second),
		WithMaxUseCount(1),
		WithReservations(),
		WithLogger(logger))
	require.NoError(t, err)
	defer ts.Shutdown(ctx)

	ts.ConfigureSchedule(Schedule{IdleTime: time.Minute})

	go configureScaleTransitions(ctx, group)

	// keep calling Capacity() which should log an error if there's a
	// regression with the potential calc
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
				ts.Capacity()
				time.Sleep(5 * time.Millisecond)
			}
		}
	}()

	var release []string
	acquire := func() {
		key := fmt.Sprintf("key-%d", rand.Int63())

		require.NoError(t, ts.Reserve(key))
		_, err := ts.Acquire(ctx, key)
		require.NoError(t, err)

		release = append(release, key)
	}

	// acquire
	for i := 0; i < 5; i++ {
		acquire()
	}

	// flood with out-of-band instances
	for i := 0; i < 50; i++ {
		group.AddInstance(provider.StateRunning)
	}

	waitForIdleCapacity(t, ctx, ts, 50, true)

	// acquire
	for i := 0; i < 10; i++ {
		acquire()
	}

	// remove all instances out of band
	for _, instance := range group.List() {
		group.RemoveInstance(instance.ID)

		ts.Capacity()
	}

	waitForIdleCapacity(t, ctx, ts, 0, false)

	// wait 10 seconds before releasing and shutting down
	time.Sleep(10 * time.Second)
	for _, key := range release {
		ts.Release(key)
	}
	ts.Shutdown(ctx)

	require.NotContains(t, buf.String(), "capacity potential below zero")
}

func configureScaleTransitions(ctx context.Context, group *dummy.InstanceGroup) {
	for {
		if ctx.Err() != nil {
			return
		}

		for _, inst := range group.List() {
			if inst.State == provider.StateCreating {
				group.SetInstanceState(inst.ID, provider.StateRunning)
			}

			if inst.State == provider.StateDeleting {
				group.SetInstanceState(inst.ID, provider.StateDeleted)
			}
		}
	}
}

func waitForIdleCapacity(t *testing.T, ctx context.Context, ts Taskscaler, n int, scaleUp bool) {
	t.Helper()

	instances := &ts.(*taskscaler).instances

	for {
		select {
		case <-ctx.Done():
			t.Error(ctx.Err())
			return

		default:
			// we use the instances capacity, which ignores reserved capacity
			idle, _, _ := instances.Capacity()
			if scaleUp {
				if idle >= n {
					return
				}
			} else {
				if idle <= n {
					return
				}
			}
			time.Sleep(time.Millisecond)
		}
	}
}
